node-red-contrib-homekit-bridged 2.0.0-dev.0 → 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.
Files changed (125) hide show
  1. package/build/lib/HAPHostNode.js +185 -148
  2. package/build/lib/HAPServiceNode.js +200 -177
  3. package/build/lib/HAPServiceNode2.js +208 -177
  4. package/build/lib/NRCHKBError.js +23 -2
  5. package/build/lib/PairingQRCode.js +62 -0
  6. package/build/lib/Storage.js +152 -90
  7. package/build/lib/api.js +654 -291
  8. package/build/lib/camera/CameraControl.js +125 -0
  9. package/build/lib/camera/CameraDelegate.js +507 -0
  10. package/build/lib/camera/MP4StreamingServer.js +159 -0
  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 +95 -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 +433 -371
  45. package/build/lib/utils/ServiceUtils2.js +519 -305
  46. package/build/lib/utils/index.js +11 -12
  47. package/build/nodes/bridge.html +206 -168
  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 +1753 -117
  51. package/build/nodes/nrchkb.js +66 -88
  52. package/build/nodes/plugin-instance.html +509 -0
  53. package/build/nodes/plugin-instance.js +46 -0
  54. package/build/nodes/service.html +557 -306
  55. package/build/nodes/service.js +5 -6
  56. package/build/nodes/service2.html +1735 -455
  57. package/build/nodes/service2.js +5 -8
  58. package/build/nodes/standalone.html +208 -176
  59. package/build/nodes/standalone.js +27 -9
  60. package/build/nodes/status.html +51 -18
  61. package/build/nodes/status.js +47 -41
  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 -2139
  72. package/examples/demo/02 - Air Purifier.json +12 -61
  73. package/examples/demo/03 - Air Quality sensor with Battery.json +8 -36
  74. package/examples/demo/04 - Dimmable Bulb.json +4 -21
  75. package/examples/demo/05 - Color Bulb (HSV).json +5 -26
  76. package/examples/demo/06 - Fan (simple, 3 speeds).json +6 -31
  77. package/examples/demo/07 - Fan (with speed, oscillate, rotation direction).json +4 -21
  78. package/examples/demo/08 - CO2 detector.json +8 -39
  79. package/examples/demo/09 - CO (carbon monoxide) example.json +9 -44
  80. package/examples/demo/10 - Door window contact sensor.json +8 -39
  81. package/examples/demos (advanced)/01 - Television with inputs and speaker.json +19 -85
  82. package/examples/switch/01 - Plain Switch.json +179 -199
  83. package/package.json +48 -34
  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/hap/HAPCharacteristic.d.ts +0 -9
  91. package/build/lib/hap/HAPService.d.ts +0 -6
  92. package/build/lib/hap/eve-app/EveCharacteristics.d.ts +0 -20
  93. package/build/lib/hap/eve-app/EveServices.d.ts +0 -5
  94. package/build/lib/types/AccessoryInformationType.d.ts +0 -11
  95. package/build/lib/types/CameraConfigType.d.ts +0 -24
  96. package/build/lib/types/CustomCharacteristicType.d.ts +0 -6
  97. package/build/lib/types/HAPHostConfigType.d.ts +0 -22
  98. package/build/lib/types/HAPHostNodeType.d.ts +0 -14
  99. package/build/lib/types/HAPService2ConfigType.d.ts +0 -6
  100. package/build/lib/types/HAPService2NodeType.d.ts +0 -7
  101. package/build/lib/types/HAPServiceConfigType.d.ts +0 -26
  102. package/build/lib/types/HAPServiceNodeType.d.ts +0 -38
  103. package/build/lib/types/HAPStatusConfigType.d.ts +0 -5
  104. package/build/lib/types/HAPStatusNodeType.d.ts +0 -12
  105. package/build/lib/types/HostType.d.ts +0 -5
  106. package/build/lib/types/NodeType.d.ts +0 -3
  107. package/build/lib/types/PublishTimersType.d.ts +0 -4
  108. package/build/lib/types/hap-nodejs/HapAdaptiveLightingControllerMode.d.ts +0 -5
  109. package/build/lib/types/hap-nodejs/HapCategories.d.ts +0 -41
  110. package/build/lib/types/storage/SerializedHostType.d.ts +0 -5
  111. package/build/lib/types/storage/StorageType.d.ts +0 -8
  112. package/build/lib/utils/AccessoryUtils.d.ts +0 -1
  113. package/build/lib/utils/BridgeUtils.d.ts +0 -1
  114. package/build/lib/utils/CharacteristicUtils.d.ts +0 -1
  115. package/build/lib/utils/CharacteristicUtils2.d.ts +0 -1
  116. package/build/lib/utils/NodeStatusUtils.d.ts +0 -17
  117. package/build/lib/utils/ServiceUtils.d.ts +0 -1
  118. package/build/lib/utils/ServiceUtils2.d.ts +0 -1
  119. package/build/lib/utils/index.d.ts +0 -1
  120. package/build/nodes/bridge.d.ts +0 -1
  121. package/build/nodes/nrchkb.d.ts +0 -1
  122. package/build/nodes/service.d.ts +0 -1
  123. package/build/nodes/service2.d.ts +0 -1
  124. package/build/nodes/standalone.d.ts +0 -1
  125. package/build/nodes/status.d.ts +0 -1
@@ -1,318 +1,532 @@
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 NRCHKBError_1 = __importDefault(require("../NRCHKBError"));
9
- const Storage_1 = require("../Storage");
10
- module.exports = function (node) {
11
- const log = (0, logger_1.logger)('NRCHKB', 'ServiceUtils2', node.config.name, node);
12
- const ServiceUtilsLegacy = require('./ServiceUtils')(node);
13
- const HapNodeJS = require('@homebridge/hap-nodejs');
14
- const Service = HapNodeJS.Service;
15
- const Characteristic = HapNodeJS.Characteristic;
16
- const NO_RESPONSE_MSG = 'NO_RESPONSE';
17
- const output = function (allCharacteristics, event, { oldValue, newValue }, connection) {
18
- var _a, _b, _c, _d, _e;
19
- const eventObject = typeof event === 'object' ? event : { name: event };
20
- log.debug(`${eventObject.name} event, oldValue: ${oldValue}, newValue: ${newValue}, connection ${connection === null || connection === void 0 ? void 0 : connection.sessionID}`);
21
- const msg = {
22
- name: node.name,
23
- topic: node.config.topic ? node.config.topic : node.topic_in,
24
- };
25
- msg.payload = {};
26
- msg.hap = {
27
- event: eventObject,
28
- allChars: allCharacteristics.reduce((allChars, singleChar) => {
29
- const cKey = singleChar.constructor.name;
30
- allChars[cKey] = singleChar.value;
31
- return allChars;
32
- }, {}),
33
- oldValue,
34
- };
35
- const key = this.constructor.name;
36
- msg.hap.reachable = (_a = node.reachable) !== null && _a !== void 0 ? _a : (_b = node.parentNode) === null || _b === void 0 ? void 0 : _b.reachable;
37
- if (msg.hap.reachable === false) {
38
- ;
39
- [node, ...((_c = node.childNodes) !== null && _c !== void 0 ? _c : [])].forEach((n) => n.nodeStatusUtils.setStatus({
40
- fill: 'red',
41
- shape: 'ring',
42
- text: 'Not reachable',
43
- type: 'NO_RESPONSE',
44
- }));
45
- }
46
- else {
47
- msg.hap.newValue = newValue;
48
- node.nodeStatusUtils.setStatus({
49
- fill: 'yellow',
50
- shape: 'dot',
51
- text: `[${eventObject.name}] ${key}${newValue != undefined ? `: ${newValue}` : ''}`,
52
- }, 3000);
53
- (_d = node.childNodes) === null || _d === void 0 ? void 0 : _d.forEach((n) => n.nodeStatusUtils.clearStatusByType('NO_RESPONSE'));
54
- (_e = node.parentNode) === null || _e === void 0 ? void 0 : _e.nodeStatusUtils.clearStatusByType('NO_RESPONSE');
55
- }
56
- msg.payload[key] = newValue;
57
- if (connection) {
58
- msg.hap.session = {
59
- sessionID: connection.sessionID,
60
- username: connection.username,
61
- remoteAddress: connection.remoteAddress,
62
- localAddress: connection.localAddress,
63
- httpPort: connection.remotePort,
64
- };
65
- }
66
- log.debug(`${node.name} received ${eventObject.name} ${key}: ${newValue}`);
67
- if (connection || node.hostNode.config.allowMessagePassthrough) {
68
- node.send(msg);
69
- }
70
- };
71
- const onCharacteristicGet = (allCharacteristics) => function (callback, _context, connection) {
72
- const characteristic = this;
73
- const oldValue = characteristic.value;
74
- const delayedCallback = (value) => {
75
- var _a;
76
- const newValue = value !== null && value !== void 0 ? value : characteristic.value;
77
- if (callback) {
78
- try {
79
- callback(((_a = node.parentNode) !== null && _a !== void 0 ? _a : node).reachable
80
- ? null
81
- : new hap_nodejs_1.HapStatusError(-70402), newValue);
82
- }
83
- catch (_) { }
84
- }
85
- output.call(characteristic, allCharacteristics, {
86
- name: "get",
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
- const onCharacteristicChange = (allCharacteristics) => function (change) {
121
- const { oldValue, newValue, context, originator, reason } = change;
122
- if (oldValue != newValue) {
123
- output.call(this, allCharacteristics, {
124
- name: "change",
125
- context: { reason, key: this.displayName },
126
- }, { oldValue, newValue, context }, originator);
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
- const onInput = function (msg) {
130
- var _a, _b;
131
- if (msg.payload) {
132
- const type = typeof msg.payload;
133
- if (type !== 'object') {
134
- log.error(`Invalid payload type: ${type}`);
135
- return;
136
- }
137
- }
138
- else {
139
- log.error('Invalid message (payload missing)');
140
- return;
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 &&
157
- Storage_1.Storage.uuid4Validate(key)) {
158
- const callbackID = key;
159
- const callbackValue = (_a = msg.payload) === null || _a === void 0 ? void 0 : _a[key];
160
- const eventCallback = Storage_1.Storage.loadCallback(callbackID);
161
- if (eventCallback) {
162
- log.debug(`Calling ${eventCallback.event} callback ${callbackID}`);
163
- eventCallback.callback(callbackValue);
164
- }
165
- else {
166
- log.error(`Callback ${callbackID} timeout`);
167
- }
168
- }
169
- else if (key === 'AdaptiveLightingController' &&
170
- node.adaptiveLightingController) {
171
- const value = (_b = msg.payload) === null || _b === void 0 ? void 0 : _b[key];
172
- const event = value === null || value === void 0 ? void 0 : value.event;
173
- if (event === 'disable') {
174
- (_c = node.adaptiveLightingController) === null || _c === void 0 ? void 0 : _c.disableAdaptiveLighting();
175
- }
176
- }
177
- else {
178
- log.error(`Instead of '${key}' try one of these characteristics: '${node.supported.join("', '")}'`);
179
- }
180
- }
181
- else {
182
- const value = (_d = msg.payload) === null || _d === void 0 ? void 0 : _d[key];
183
- const parentNode = (_e = node.parentNode) !== null && _e !== void 0 ? _e : node;
184
- parentNode.reachable = value !== NO_RESPONSE_MSG;
185
- const characteristic = node.service.getCharacteristic(Characteristic[key]);
186
- if (context !== null) {
187
- characteristic.setValue(value, context);
188
- }
189
- else {
190
- characteristic.setValue(value);
191
- }
192
- }
193
- });
194
- };
195
- const onClose = function (removed, done) {
196
- const characteristics = node.service.characteristics.concat(node.service.optionalCharacteristics);
197
- characteristics.forEach(function (characteristic) {
198
- characteristic.removeListener('get', node.onCharacteristicGet);
199
- characteristic.removeListener('set', node.onCharacteristicSet);
200
- 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"
201
123
  });
202
- if (node.config.isParent) {
203
- node.accessory.removeListener('identify', node.onIdentify);
204
- }
205
- if (removed) {
206
- if (node.config.isParent) {
207
- node.hostNode.host.removeBridgedAccessories([node.accessory]);
208
- node.accessory.destroy();
209
- }
210
- else {
211
- node.accessory.removeService(node.service);
212
- node.parentService.removeLinkedService(node.service);
213
- }
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 (_) {
214
179
  }
215
- done();
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
+ );
216
191
  };
217
- const getOrCreate = function (accessory, serviceInformation, parentService) {
218
- const newService = new Service[serviceInformation.serviceName](serviceInformation.name, serviceInformation.UUID);
219
- log.debug(`Looking for service with UUID ${serviceInformation.UUID} ...`);
220
- let service = accessory.services.find((service) => {
221
- return newService.subtype === service.subtype;
222
- });
223
- if (service && newService.UUID !== service.UUID) {
224
- log.debug('... service type changed! Removing the old service.');
225
- accessory.removeService(service);
226
- service = undefined;
227
- }
228
- if (!service) {
229
- log.debug(`... didn't find it. Adding new ${serviceInformation.serviceName} service.`);
230
- if (serviceInformation.serviceName === 'CameraControl') {
231
- configureCameraSource(accessory, newService, serviceInformation.config);
232
- service = newService;
233
- }
234
- else {
235
- service = accessory.addService(newService);
236
- }
237
- }
238
- else {
239
- log.debug('... found it! Updating it.');
240
- service
241
- .getCharacteristic(Characteristic.Name)
242
- .setValue(serviceInformation.name);
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
+ );
243
321
  }
244
- if (parentService) {
245
- if (serviceInformation.serviceName === 'CameraControl') {
246
- log.debug('... and adding service to accessory.');
247
- }
248
- else if (service) {
249
- log.debug('... and linking service to parent.');
250
- parentService.addLinkedService(service);
251
- }
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);
252
334
  }
253
- return service;
254
- };
255
- const configureCameraSource = function (_accessory, _service, config) {
256
- if (config.cameraConfigSource) {
257
- log.debug('Configuring Camera Source');
258
- if (!config.cameraConfigVideoProcessor) {
259
- log.error('Missing configuration for CameraControl: videoProcessor cannot be empty!');
260
- }
261
- else {
262
- }
263
- }
264
- else {
265
- log.error('Missing configuration for CameraControl.');
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
+ );
266
426
  }
267
- };
268
- const waitForParent = () => {
269
- log.debug('Waiting for Parent Service');
270
- return new Promise((resolve) => {
271
- node.nodeStatusUtils.setStatus({
272
- fill: 'blue',
273
- shape: 'dot',
274
- text: 'Waiting for Parent Service',
275
- });
276
- const checkAndWait = () => {
277
- const parentNode = node.RED.nodes.getNode(node.config.parentService);
278
- if (parentNode && parentNode.configured) {
279
- resolve(parentNode);
280
- }
281
- else {
282
- setTimeout(checkAndWait, 1000);
283
- }
284
- };
285
- checkAndWait();
286
- }).catch((error) => {
287
- log.error(`Waiting for Parent Service failed due to: ${error}`);
288
- throw new NRCHKBError_1.default(error);
427
+ const attachedService = await pluginDefinition.factory.attach({
428
+ ...createAttachContext(pluginConfig.config)
289
429
  });
290
- };
291
- const handleWaitForSetup = (config, msg, resolve) => {
292
- if (node.setupDone) {
293
- return;
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);
294
494
  }
295
- if (msg.hasOwnProperty('payload') &&
296
- msg.payload.hasOwnProperty('nrchkb') &&
297
- msg.payload.nrchkb.hasOwnProperty('setup')) {
298
- node.setupDone = true;
299
- const newConfig = Object.assign(Object.assign({}, config), msg.payload.nrchkb.setup);
300
- node.removeListener('input', node.handleWaitForSetup);
301
- resolve(newConfig);
302
- }
303
- else {
304
- log.error('Invalid message (required {"payload":{"nrchkb":{"setup":{}}}})');
305
- }
306
- };
307
- return {
308
- getOrCreate,
309
- onCharacteristicGet,
310
- onCharacteristicSet,
311
- onCharacteristicChange,
312
- onInput,
313
- onClose,
314
- waitForParent,
315
- handleWaitForSetup,
316
- configureAdaptiveLightning: ServiceUtilsLegacy.configureAdaptiveLightning,
317
- };
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
+ };
318
531
  };
532
+ module.exports = buildServiceUtils2;