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.
Files changed (128) hide show
  1. package/build/lib/HAPHostNode.js +183 -141
  2. package/build/lib/HAPServiceNode.js +199 -172
  3. package/build/lib/HAPServiceNode2.js +207 -172
  4. package/build/lib/NRCHKBError.js +23 -2
  5. package/build/lib/PairingQRCode.js +62 -0
  6. package/build/lib/Storage.js +157 -92
  7. package/build/lib/api.js +654 -288
  8. package/build/lib/camera/CameraControl.js +119 -84
  9. package/build/lib/camera/CameraDelegate.js +481 -404
  10. package/build/lib/camera/MP4StreamingServer.js +148 -139
  11. package/build/lib/hap/HAPCharacteristic.js +25 -4
  12. package/build/lib/hap/HAPService.js +25 -4
  13. package/build/lib/hap/eve-app/EveCharacteristics.js +124 -81
  14. package/build/lib/hap/eve-app/EveServices.js +50 -17
  15. package/build/lib/hap/hap-nodejs.js +32 -0
  16. package/build/lib/migration/HomeKitService2Migration.js +34 -0
  17. package/build/lib/migration/NodeMigration.js +75 -0
  18. package/build/lib/types/AccessoryInformationType.js +15 -1
  19. package/build/lib/types/CameraConfigType.js +15 -1
  20. package/build/lib/types/CustomCharacteristicType.js +15 -1
  21. package/build/lib/types/HAPHostConfigType.js +15 -1
  22. package/build/lib/types/HAPHostNodeType.js +15 -1
  23. package/build/lib/types/HAPService2ConfigType.js +15 -1
  24. package/build/lib/types/HAPService2NodeType.js +15 -1
  25. package/build/lib/types/HAPServiceConfigType.js +15 -1
  26. package/build/lib/types/HAPServiceNodeType.js +15 -1
  27. package/build/lib/types/HAPStatusConfigType.js +15 -1
  28. package/build/lib/types/HAPStatusNodeType.js +15 -1
  29. package/build/lib/types/HostType.js +28 -7
  30. package/build/lib/types/NodeType.js +15 -1
  31. package/build/lib/types/PublishTimersType.js +15 -1
  32. package/build/lib/types/UniFiControllerConfigType.js +16 -0
  33. package/build/lib/types/hap-nodejs/HapAdaptiveLightingControllerMode.js +28 -7
  34. package/build/lib/types/hap-nodejs/HapCategories.js +64 -43
  35. package/build/lib/types/storage/SerializedHostType.js +15 -1
  36. package/build/lib/types/storage/StorageType.js +34 -10
  37. package/build/lib/unifi/ProtectDiscovery.js +80 -0
  38. package/build/lib/utils/AccessoryUtils.js +152 -110
  39. package/build/lib/utils/BridgeUtils.js +82 -39
  40. package/build/lib/utils/CharacteristicUtils.js +5 -49
  41. package/build/lib/utils/CharacteristicUtils2.js +5 -49
  42. package/build/lib/utils/CharacteristicUtilsBase.js +81 -0
  43. package/build/lib/utils/NodeStatusUtils.js +89 -40
  44. package/build/lib/utils/ServiceUtils.js +434 -375
  45. package/build/lib/utils/ServiceUtils2.js +514 -309
  46. package/build/lib/utils/index.js +10 -11
  47. package/build/nodes/bridge.html +184 -166
  48. package/build/nodes/bridge.js +27 -9
  49. package/build/nodes/locales/en-US/node-red-contrib-homekit-bridged.json +22 -0
  50. package/build/nodes/nrchkb.html +1601 -88
  51. package/build/nodes/nrchkb.js +66 -88
  52. package/build/nodes/plugin-instance.html +499 -0
  53. package/build/nodes/plugin-instance.js +46 -0
  54. package/build/nodes/service.html +517 -299
  55. package/build/nodes/service.js +5 -6
  56. package/build/nodes/service2.html +1683 -460
  57. package/build/nodes/service2.js +5 -8
  58. package/build/nodes/standalone.html +187 -174
  59. package/build/nodes/standalone.js +27 -9
  60. package/build/nodes/status.html +51 -18
  61. package/build/nodes/status.js +47 -40
  62. package/build/nodes/unifi-controller.html +92 -0
  63. package/build/nodes/unifi-controller.js +20 -0
  64. package/build/plugins/embedded/homebridge-camera-ffmpeg/index.js +479 -0
  65. package/build/plugins/embedded/homebridge-unifi-protect/index.js +521 -0
  66. package/build/plugins/embedded/index.js +58 -0
  67. package/build/plugins/nrchkb-homekit-plugins.js +17 -0
  68. package/build/plugins/registry/index.js +203 -0
  69. package/build/plugins/registry/types.js +16 -0
  70. package/build/scripts/migrate-homekit-service-flows.js +47 -0
  71. package/examples/demo/01 - ALL Demos single import.json +1885 -1885
  72. package/examples/demo/02 - Air Purifier.json +279 -279
  73. package/examples/demo/03 - Air Quality sensor with Battery.json +254 -254
  74. package/examples/demo/04 - Dimmable Bulb.json +172 -172
  75. package/examples/demo/05 - Color Bulb (HSV).json +195 -195
  76. package/examples/demo/06 - Fan (simple, 3 speeds).json +240 -240
  77. package/examples/demo/07 - Fan (with speed, oscillate, rotation direction).json +175 -175
  78. package/examples/demo/08 - CO2 detector.json +224 -224
  79. package/examples/demo/09 - CO (carbon monoxide) example.json +255 -255
  80. package/examples/demo/10 - Door window contact sensor.json +234 -234
  81. package/examples/demos (advanced)/01 - Television with inputs and speaker.json +541 -541
  82. package/examples/switch/01 - Plain Switch.json +178 -178
  83. package/package.json +95 -84
  84. package/build/lib/HAPHostNode.d.ts +0 -1
  85. package/build/lib/HAPServiceNode.d.ts +0 -1
  86. package/build/lib/HAPServiceNode2.d.ts +0 -1
  87. package/build/lib/NRCHKBError.d.ts +0 -3
  88. package/build/lib/Storage.d.ts +0 -30
  89. package/build/lib/api.d.ts +0 -1
  90. package/build/lib/camera/CameraControl.d.ts +0 -3
  91. package/build/lib/camera/CameraDelegate.d.ts +0 -38
  92. package/build/lib/camera/MP4StreamingServer.d.ts +0 -26
  93. package/build/lib/hap/HAPCharacteristic.d.ts +0 -9
  94. package/build/lib/hap/HAPService.d.ts +0 -6
  95. package/build/lib/hap/eve-app/EveCharacteristics.d.ts +0 -20
  96. package/build/lib/hap/eve-app/EveServices.d.ts +0 -5
  97. package/build/lib/types/AccessoryInformationType.d.ts +0 -11
  98. package/build/lib/types/CameraConfigType.d.ts +0 -24
  99. package/build/lib/types/CustomCharacteristicType.d.ts +0 -6
  100. package/build/lib/types/HAPHostConfigType.d.ts +0 -22
  101. package/build/lib/types/HAPHostNodeType.d.ts +0 -14
  102. package/build/lib/types/HAPService2ConfigType.d.ts +0 -6
  103. package/build/lib/types/HAPService2NodeType.d.ts +0 -7
  104. package/build/lib/types/HAPServiceConfigType.d.ts +0 -26
  105. package/build/lib/types/HAPServiceNodeType.d.ts +0 -38
  106. package/build/lib/types/HAPStatusConfigType.d.ts +0 -5
  107. package/build/lib/types/HAPStatusNodeType.d.ts +0 -12
  108. package/build/lib/types/HostType.d.ts +0 -5
  109. package/build/lib/types/NodeType.d.ts +0 -3
  110. package/build/lib/types/PublishTimersType.d.ts +0 -4
  111. package/build/lib/types/hap-nodejs/HapAdaptiveLightingControllerMode.d.ts +0 -5
  112. package/build/lib/types/hap-nodejs/HapCategories.d.ts +0 -41
  113. package/build/lib/types/storage/SerializedHostType.d.ts +0 -5
  114. package/build/lib/types/storage/StorageType.d.ts +0 -8
  115. package/build/lib/utils/AccessoryUtils.d.ts +0 -1
  116. package/build/lib/utils/BridgeUtils.d.ts +0 -1
  117. package/build/lib/utils/CharacteristicUtils.d.ts +0 -1
  118. package/build/lib/utils/CharacteristicUtils2.d.ts +0 -1
  119. package/build/lib/utils/NodeStatusUtils.d.ts +0 -17
  120. package/build/lib/utils/ServiceUtils.d.ts +0 -1
  121. package/build/lib/utils/ServiceUtils2.d.ts +0 -1
  122. package/build/lib/utils/index.d.ts +0 -1
  123. package/build/nodes/bridge.d.ts +0 -1
  124. package/build/nodes/nrchkb.d.ts +0 -1
  125. package/build/nodes/service.d.ts +0 -1
  126. package/build/nodes/service2.d.ts +0 -1
  127. package/build/nodes/standalone.d.ts +0 -1
  128. package/build/nodes/status.d.ts +0 -1
@@ -1,322 +1,527 @@
1
1
  "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
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
- Object.defineProperty(exports, "__esModule", { value: true });
6
- const hap_nodejs_1 = require("@homebridge/hap-nodejs");
7
- const logger_1 = require("@nrchkb/logger");
8
- const CameraControl_1 = require("../camera/CameraControl");
9
- const NRCHKBError_1 = __importDefault(require("../NRCHKBError"));
10
- const Storage_1 = require("../Storage");
11
- module.exports = (node) => {
12
- const log = (0, logger_1.logger)('NRCHKB', 'ServiceUtils2', node.config.name, node);
13
- const ServiceUtilsLegacy = require('./ServiceUtils')(node);
14
- const HapNodeJS = require('@homebridge/hap-nodejs');
15
- const Service = HapNodeJS.Service;
16
- const Characteristic = HapNodeJS.Characteristic;
17
- const NO_RESPONSE_MSG = 'NO_RESPONSE';
18
- const output = function (allCharacteristics, event, { oldValue, newValue }, connection) {
19
- var _a, _b, _c, _d, _e;
20
- const eventObject = typeof event === 'object' ? event : { name: event };
21
- log.debug(`${eventObject.name} event, oldValue: ${oldValue}, newValue: ${newValue}, connection ${connection === null || connection === void 0 ? void 0 : connection.sessionID}`);
22
- const msg = {
23
- name: node.name,
24
- topic: node.config.topic ? node.config.topic : node.topic_in
25
- };
26
- msg.payload = {};
27
- msg.hap = {
28
- event: eventObject,
29
- allChars: allCharacteristics.reduce((allChars, singleChar) => {
30
- const cKey = singleChar.constructor.name;
31
- allChars[cKey] = singleChar.value;
32
- return allChars;
33
- }, {}),
34
- oldValue
35
- };
36
- const key = this.constructor.name;
37
- msg.hap.reachable = (_a = node.reachable) !== null && _a !== void 0 ? _a : (_b = node.parentNode) === null || _b === void 0 ? void 0 : _b.reachable;
38
- if (msg.hap.reachable === false) {
39
- ;
40
- [node, ...((_c = node.childNodes) !== null && _c !== void 0 ? _c : [])].forEach((n) => {
41
- n.nodeStatusUtils.setStatus({
42
- fill: 'red',
43
- shape: 'ring',
44
- text: 'Not reachable',
45
- type: 'NO_RESPONSE'
46
- });
47
- });
48
- }
49
- else {
50
- msg.hap.newValue = newValue;
51
- node.nodeStatusUtils.setStatus({
52
- fill: 'yellow',
53
- shape: 'dot',
54
- text: `[${eventObject.name}] ${key}${newValue !== undefined ? `: ${newValue}` : ''}`
55
- }, 3000);
56
- (_d = node.childNodes) === null || _d === void 0 ? void 0 : _d.forEach((n) => {
57
- n.nodeStatusUtils.clearStatusByType('NO_RESPONSE');
58
- });
59
- (_e = node.parentNode) === null || _e === void 0 ? void 0 : _e.nodeStatusUtils.clearStatusByType('NO_RESPONSE');
60
- }
61
- msg.payload[key] = newValue;
62
- if (connection) {
63
- msg.hap.session = {
64
- sessionID: connection.sessionID,
65
- username: connection.username,
66
- remoteAddress: connection.remoteAddress,
67
- localAddress: connection.localAddress,
68
- httpPort: connection.remotePort
69
- };
70
- }
71
- log.debug(`${node.name} received ${eventObject.name} ${key}: ${newValue}`);
72
- if (connection || node.hostNode.config.allowMessagePassthrough) {
73
- node.send(msg);
74
- }
75
- };
76
- const onCharacteristicGet = (allCharacteristics) => function (callback, _context, connection) {
77
- const oldValue = this.value;
78
- const delayedCallback = (value) => {
79
- var _a;
80
- const newValue = value !== null && value !== void 0 ? value : this.value;
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
- const onInput = (msg) => {
134
- var _a, _b;
135
- if (msg.payload) {
136
- const type = typeof msg.payload;
137
- if (type !== 'object') {
138
- log.error(`Invalid payload type: ${type}`);
139
- return;
140
- }
141
- }
142
- else {
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
- const onClose = (removed, done) => {
199
- const characteristics = node.service.characteristics.concat(node.service.optionalCharacteristics);
200
- characteristics.forEach((characteristic) => {
201
- characteristic.removeListener('get', node.onCharacteristicGet);
202
- characteristic.removeListener('set', node.onCharacteristicSet);
203
- characteristic.removeListener('change', node.onCharacteristicChange);
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
- if (node.config.isParent) {
206
- node.accessory.removeListener('identify', node.onIdentify);
207
- }
208
- if (removed) {
209
- if (node.config.isParent) {
210
- node.hostNode.host.removeBridgedAccessories([node.accessory]);
211
- node.accessory.destroy();
212
- }
213
- else {
214
- node.accessory.removeService(node.service);
215
- node.parentService.removeLinkedService(node.service);
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
- done();
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
- const getOrCreate = (accessory, serviceInformation, parentService) => {
221
- const newService = new Service[serviceInformation.serviceName](serviceInformation.name, serviceInformation.UUID);
222
- log.debug(`Looking for service with UUID ${serviceInformation.UUID} ...`);
223
- let service = accessory.services.find((service) => {
224
- return newService.subtype === service.subtype;
225
- });
226
- if (service && newService.UUID !== service.UUID) {
227
- log.debug('... service type changed! Removing the old service.');
228
- accessory.removeService(service);
229
- service = undefined;
230
- }
231
- if (!service) {
232
- log.debug(`... didn't find it. Adding new ${serviceInformation.serviceName} service.`);
233
- if (serviceInformation.serviceName === 'CameraControl') {
234
- configureCameraSource(accessory, newService, serviceInformation.config);
235
- service = newService;
236
- }
237
- else {
238
- service = accessory.addService(newService);
239
- }
240
- }
241
- else {
242
- log.debug('... found it! Updating it.');
243
- service
244
- .getCharacteristic(Characteristic.Name)
245
- .setValue(serviceInformation.name);
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
- if (parentService) {
248
- if (serviceInformation.serviceName === 'CameraControl') {
249
- log.debug('... and adding service to accessory.');
250
- }
251
- else if (service) {
252
- log.debug('... and linking service to parent.');
253
- parentService.addLinkedService(service);
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
- return service;
257
- };
258
- const configureCameraSource = (accessory, _service, config) => {
259
- if (config.cameraConfigSource) {
260
- log.debug('Configuring Camera Source');
261
- if (!config.cameraConfigVideoProcessor) {
262
- log.error('Missing configuration for CameraControl: videoProcessor cannot be empty!');
263
- }
264
- else {
265
- (0, CameraControl_1.configureCamera)(accessory, config);
266
- }
267
- }
268
- else {
269
- log.error('Missing configuration for CameraControl.');
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
- const waitForParent = () => {
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
- const handleWaitForSetup = (config, msg, resolve) => {
296
- if (node.setupDone) {
297
- return;
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
- if (Object.hasOwn(msg, 'payload') &&
300
- Object.hasOwn(msg.payload, 'nrchkb') &&
301
- Object.hasOwn(msg.payload.nrchkb, 'setup')) {
302
- node.setupDone = true;
303
- const newConfig = Object.assign(Object.assign({}, config), msg.payload.nrchkb.setup);
304
- node.removeListener('input', node.handleWaitForSetup);
305
- resolve(newConfig);
306
- }
307
- else {
308
- log.error('Invalid message (required {"payload":{"nrchkb":{"setup":{}}}})');
309
- }
310
- };
311
- return {
312
- getOrCreate,
313
- onCharacteristicGet,
314
- onCharacteristicSet,
315
- onCharacteristicChange,
316
- onInput,
317
- onClose,
318
- waitForParent,
319
- handleWaitForSetup,
320
- configureAdaptiveLightning: ServiceUtilsLegacy.configureAdaptiveLightning
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;