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,330 @@
1
1
  <!--suppress EqualityComparisonWithCoercionJS -->
2
2
  <script data-template-name="homekit-service" type="text/x-red">
3
- <div class="form-row">
4
- <label for="node-config-input-isParent"><i class="fa fa-tasks"></i> Service Hierarchy</label>
5
- <select id="node-config-input-isParent" style="width: 70%">
6
- <option value="true" selected="selected">Parent</option>
7
- <option value="false">Linked</option>
8
- </select>
9
- </div>
10
-
11
- <div class="form-row">
12
- <label for="node-input-serviceName">
13
- <i class="fa fa-cog"></i>
14
- Service</label>
15
- <select id="node-input-serviceName" style="width: 70%">
16
- <option value="">Choose...</option>
17
- </select>
18
- </div>
19
-
20
- <div id="isParent">
21
- <div class="form-row">
22
- <label for="node-config-input-hostType"><i class="fa fa-tasks"></i> Host Type</label>
23
- <select id="node-config-input-hostType" style="width: 70%">
24
- <option value="0" selected="selected">Bridge</option>
25
- <option value="1">Accessory</option>
26
- </select>
27
- </div>
28
- <div id="isOnBridge">
29
- <div id="isBridgeInSubflow" class="alert alert-warning" role="alert">
30
- Read more <b><a href="#" id="bridgeInSubflowNotice">here</a></b> about adding Bridge in a Subflow.
31
- </div>
32
- <div class="form-row" style="height: 34px;">
33
- <label for="node-input-bridge">
34
- <i class="fa fa-rocket"></i>
35
- Bridge</label>
36
- <input id="node-input-bridge">
37
- </div>
38
- <div class="form-row">
39
- <label for="node-input-manufacturer"><i class="fa fa-wrench"></i> Manufacturer</label>
40
- <input type="text" id="node-input-manufacturer" placeholder="Manufacturer">
3
+ <div class="nrchkb-editor">
4
+ <div class="alert alert-warning nrchkb-warning" role="alert">
5
+ <strong>Deprecated:</strong> homekit-service will be disabled soon. Migrate this node to homekit-service2 to keep it working in NRCHKB 2.x.
6
+ <div style="margin-top: 8px;">
7
+ <button type="button" class="red-ui-button" id="nrchkb-migrate-service2">
8
+ <i class="fa fa-exchange"></i> Migrate this node
9
+ </button>
41
10
  </div>
42
- <div class="form-row">
43
- <label for="node-input-serialNo"><i class="fa fa-wrench"></i> Serial Number</label>
44
- <input type="text" id="node-input-serialNo" placeholder="Serial Number">
11
+ <div id="nrchkb-migration-result" class="alert alert-info nrchkb-info" role="alert" style="display: none; margin-top: 8px;"></div>
12
+ </div>
13
+
14
+ <details class="nrchkb-section" open>
15
+ <summary><i class="fa fa-sliders"></i> Essentials</summary>
16
+ <div class="nrchkb-section-body">
17
+ <div class="form-row">
18
+ <label for="node-config-input-isParent"><i class="fa fa-sitemap"></i> Service Hierarchy</label>
19
+ <select id="node-config-input-isParent">
20
+ <option value="true" selected="selected">Parent</option>
21
+ <option value="false">Linked</option>
22
+ </select>
23
+ </div>
24
+
25
+ <div class="form-row">
26
+ <label for="node-input-serviceName">
27
+ <i class="fa fa-puzzle-piece"></i>
28
+ Service
29
+ </label>
30
+ <select id="node-input-serviceName">
31
+ <option value="">Choose...</option>
32
+ </select>
33
+ </div>
34
+
35
+ <div class="form-row">
36
+ <label for="node-input-name">
37
+ <i class="fa fa-tag"></i>
38
+ Name
39
+ </label>
40
+ <input type="text" id="node-input-name" placeholder="Name">
41
+ </div>
45
42
  </div>
46
- <div class="form-row">
47
- <label for="node-input-model"><i class="fa fa-wrench"></i> Model</label>
48
- <input type="text" id="node-input-model" placeholder="Model">
43
+ </details>
44
+
45
+ <details class="nrchkb-section" open>
46
+ <summary><i class="fa fa-map-marker"></i> Placement</summary>
47
+ <div class="nrchkb-section-body">
48
+ <div id="isParent">
49
+ <div class="form-row">
50
+ <label for="node-config-input-hostType"><i class="fa fa-server"></i> Host Type</label>
51
+ <select id="node-config-input-hostType">
52
+ <option value="0" selected="selected">Bridge</option>
53
+ <option value="1">Accessory</option>
54
+ </select>
55
+ </div>
56
+ <div id="isOnBridge">
57
+ <div id="isBridgeInSubflow" class="alert alert-warning nrchkb-warning" role="alert">
58
+ Read more <b><a href="#" id="bridgeInSubflowNotice">here</a></b> about adding Bridge in a Subflow.
59
+ </div>
60
+ <div class="form-row">
61
+ <label for="node-input-bridge">
62
+ <i class="fa fa-link"></i>
63
+ Bridge
64
+ </label>
65
+ <input id="node-input-bridge">
66
+ </div>
67
+ <details class="nrchkb-section">
68
+ <summary><i class="fa fa-id-card-o"></i> Accessory Metadata</summary>
69
+ <div class="nrchkb-section-body">
70
+ <div class="form-row">
71
+ <label for="node-input-manufacturer"><i class="fa fa-industry"></i> Manufacturer</label>
72
+ <input type="text" id="node-input-manufacturer" placeholder="Manufacturer">
73
+ </div>
74
+ <div class="form-row">
75
+ <label for="node-input-serialNo"><i class="fa fa-barcode"></i> Serial Number</label>
76
+ <input type="text" id="node-input-serialNo" placeholder="Serial Number">
77
+ </div>
78
+ <div class="form-row">
79
+ <label for="node-input-model"><i class="fa fa-cube"></i> Model</label>
80
+ <input type="text" id="node-input-model" placeholder="Model">
81
+ </div>
82
+ <div class="form-row">
83
+ <label for="node-input-firmwareRev"><i class="fa fa-microchip"></i> Firmware Revision</label>
84
+ <input type="text" id="node-input-firmwareRev" placeholder="Firmware Revision">
85
+ </div>
86
+ <div class="form-row">
87
+ <label for="node-input-hardwareRev"><i class="fa fa-hdd-o"></i> Hardware Revision</label>
88
+ <input type="text" id="node-input-hardwareRev" placeholder="Hardware Revision">
89
+ </div>
90
+ <div class="form-row">
91
+ <label for="node-input-softwareRev"><i class="fa fa-code"></i> Software Revision</label>
92
+ <input type="text" id="node-input-softwareRev" placeholder="Software Revision">
93
+ </div>
94
+ </div>
95
+ </details>
96
+ </div>
97
+ <div id="isAccessory">
98
+ <div class="form-row">
99
+ <label for="node-input-accessoryId">
100
+ <i class="fa fa-home"></i>
101
+ Accessory
102
+ </label>
103
+ <input id="node-input-accessoryId">
104
+ </div>
105
+ </div>
106
+ </div>
107
+
108
+ <div id="isLinked" style="display: none;">
109
+ <div class="form-row">
110
+ <label for="node-input-parentService">
111
+ <i class="fa fa-level-up"></i>
112
+ Parent Service
113
+ </label>
114
+ <select id="node-input-parentService">
115
+ <option value="">Choose...</option>
116
+ </select>
117
+ </div>
118
+ </div>
49
119
  </div>
50
- <div class="form-row">
51
- <label for="node-input-firmwareRev"><i class="fa fa-wrench"></i> Firmware Revision</label>
52
- <input type="text" id="node-input-firmwareRev" placeholder="Firmware Revision">
120
+ </details>
121
+
122
+ <details class="nrchkb-section" open>
123
+ <summary><i class="fa fa-exchange"></i> Message Routing</summary>
124
+ <div class="nrchkb-section-body">
125
+ <div class="form-row">
126
+ <label for="node-input-topic"><i class="fa fa-comment-o"></i> Topic</label>
127
+ <input type="text" id="node-input-topic" placeholder="Topic">
128
+ </div>
129
+ <div class="form-row nrchkb-checkbox-row">
130
+ <label class="nrchkb-checkbox-label" for="node-input-filter">
131
+ <input type="checkbox" id="node-input-filter">
132
+ <span><i class="fa fa-filter"></i> Filter on Topic</span>
133
+ </label>
134
+ </div>
53
135
  </div>
54
- <div class="form-row">
55
- <label for="node-input-hardwareRev"><i class="fa fa-wrench"></i> Hardware Revision</label>
56
- <input type="text" id="node-input-hardwareRev" placeholder="Hardware Revision">
136
+ </details>
137
+
138
+ <details id="camera-configuration" class="nrchkb-section" open style="display: none;">
139
+ <summary><i class="fa fa-video-camera"></i> Camera Configuration</summary>
140
+ <div class="nrchkb-section-body">
141
+ <div class="form-row">
142
+ <label for="node-input-cameraConfigVideoProcessor"><i class="fa fa-terminal"></i> Video Processor</label>
143
+ <input type="text" id="node-input-cameraConfigVideoProcessor" placeholder="ffmpeg">
144
+ </div>
145
+ <div class="form-row">
146
+ <label for="node-input-cameraConfigSource"><i class="fa fa-sign-in"></i> Source</label>
147
+ <input type="text" id="node-input-cameraConfigSource" placeholder="">
148
+ </div>
149
+ <div class="form-row">
150
+ <label for="node-input-cameraConfigStillImageSource"><i class="fa fa-picture-o"></i> Still Image Source</label>
151
+ <input type="text" id="node-input-cameraConfigStillImageSource" placeholder="">
152
+ </div>
153
+ <div class="form-row">
154
+ <label for="node-input-cameraConfigMaxStreams"><i class="fa fa-random"></i> Max Streams</label>
155
+ <input type="text" id="node-input-cameraConfigMaxStreams" placeholder="">
156
+ </div>
157
+ <div class="form-row">
158
+ <label for="node-input-cameraConfigMaxWidth"><i class="fa fa-arrows-h"></i> Max Width</label>
159
+ <input type="text" id="node-input-cameraConfigMaxWidth" placeholder="">
160
+ </div>
161
+ <div class="form-row">
162
+ <label for="node-input-cameraConfigMaxHeight"><i class="fa fa-arrows-v"></i> Max Height</label>
163
+ <input type="text" id="node-input-cameraConfigMaxHeight" placeholder="">
164
+ </div>
165
+ <div class="form-row">
166
+ <label for="node-input-cameraConfigMaxFPS"><i class="fa fa-clock-o"></i> Max FPS</label>
167
+ <input type="text" id="node-input-cameraConfigMaxFPS" placeholder="">
168
+ </div>
169
+ <div class="form-row">
170
+ <label for="node-input-cameraConfigMaxBitrate"><i class="fa fa-tachometer"></i> Max Bitrate</label>
171
+ <input type="text" id="node-input-cameraConfigMaxBitrate" placeholder="">
172
+ </div>
173
+ <div class="form-row">
174
+ <label for="node-input-cameraConfigVideoCodec"><i class="fa fa-film"></i> Video Codec</label>
175
+ <input type="text" id="node-input-cameraConfigVideoCodec" placeholder="">
176
+ </div>
177
+ <div class="form-row">
178
+ <label for="node-input-cameraConfigAudioCodec"><i class="fa fa-volume-up"></i> Audio Codec</label>
179
+ <input type="text" id="node-input-cameraConfigAudioCodec" placeholder="">
180
+ </div>
181
+ <div class="form-row nrchkb-checkbox-row">
182
+ <label class="nrchkb-checkbox-label" for="node-input-cameraConfigAudio">
183
+ <input type="checkbox" id="node-input-cameraConfigAudio">
184
+ <span><i class="fa fa-headphones"></i> Audio</span>
185
+ </label>
186
+ </div>
187
+ <div class="form-row">
188
+ <label for="node-input-cameraConfigPacketSize"><i class="fa fa-archive"></i> Packet Size</label>
189
+ <input type="text" id="node-input-cameraConfigPacketSize" placeholder="">
190
+ </div>
191
+ <div class="form-row nrchkb-checkbox-row">
192
+ <label class="nrchkb-checkbox-label" for="node-input-cameraConfigVerticalFlip">
193
+ <input type="checkbox" id="node-input-cameraConfigVerticalFlip">
194
+ <span><i class="fa fa-arrows-v"></i> Vertical Flip</span>
195
+ </label>
196
+ </div>
197
+ <div class="form-row nrchkb-checkbox-row">
198
+ <label class="nrchkb-checkbox-label" for="node-input-cameraConfigHorizontalFlip">
199
+ <input type="checkbox" id="node-input-cameraConfigHorizontalFlip">
200
+ <span><i class="fa fa-arrows-h"></i> Horizontal Flip</span>
201
+ </label>
202
+ </div>
203
+ <div class="form-row">
204
+ <label for="node-input-cameraConfigMapVideo"><i class="fa fa-film"></i> Map Video</label>
205
+ <input type="text" id="node-input-cameraConfigMapVideo" placeholder="">
206
+ </div>
207
+ <div class="form-row">
208
+ <label for="node-input-cameraConfigMapAudio"><i class="fa fa-volume-up"></i> Map Audio</label>
209
+ <input type="text" id="node-input-cameraConfigMapAudio" placeholder="">
210
+ </div>
211
+ <div class="form-row">
212
+ <label for="node-input-cameraConfigVideoFilter"><i class="fa fa-filter"></i> Video Filter</label>
213
+ <input type="text" id="node-input-cameraConfigVideoFilter" placeholder="">
214
+ </div>
215
+ <div class="form-row">
216
+ <label for="node-input-cameraConfigAdditionalCommandLine"><i class="fa fa-terminal"></i> Additional Command Line</label>
217
+ <input type="text" id="node-input-cameraConfigAdditionalCommandLine" placeholder="">
218
+ </div>
219
+ <div class="form-row nrchkb-checkbox-row">
220
+ <label class="nrchkb-checkbox-label" for="node-input-cameraConfigDebug">
221
+ <input type="checkbox" id="node-input-cameraConfigDebug">
222
+ <span><i class="fa fa-bug"></i> Debug</span>
223
+ </label>
224
+ </div>
225
+
226
+ <div class="form-row">
227
+ <label for="node-input-cameraConfigSnapshotOutput"><i class="fa fa-camera"></i> Snapshot output</label>
228
+ <select id="node-input-cameraConfigSnapshotOutput">
229
+ <option value="disabled" selected="selected">Disabled</option>
230
+ <option value="path">Path</option>
231
+ <option value="content">Content</option>
232
+ </select>
233
+ </div>
234
+
235
+ <div class="form-row">
236
+ <label for="node-input-cameraConfigInterfaceName"><i class="fa fa-sitemap"></i> Interface Name</label>
237
+ <input type="text" id="node-input-cameraConfigInterfaceName" placeholder="">
238
+ </div>
57
239
  </div>
58
- <div class="form-row">
59
- <label for="node-input-softwareRev"><i class="fa fa-wrench"></i> Software Revision</label>
60
- <input type="text" id="node-input-softwareRev" placeholder="Software Revision">
240
+ </details>
241
+
242
+ <details id="adaptive-lightning-configuration" class="nrchkb-section" open style="display: none;">
243
+ <summary><i class="fa fa-sun-o"></i> Adaptive Lighting Configuration</summary>
244
+ <div class="nrchkb-section-body">
245
+ <div class="form-row nrchkb-checkbox-row">
246
+ <label class="nrchkb-checkbox-label" for="node-input-adaptiveLightingOptionsEnable">
247
+ <input type="checkbox" id="node-input-adaptiveLightingOptionsEnable">
248
+ <span><i class="fa fa-toggle-on"></i> Enable</span>
249
+ </label>
250
+ </div>
251
+ <div class="form-row">
252
+ <label for="node-input-adaptiveLightingOptionsMode"><i class="fa fa-magic"></i> Mode</label>
253
+ <select id="node-input-adaptiveLightingOptionsMode">
254
+ <option value="" selected hidden disabled>AUTOMATIC</option>
255
+ <option value="1">AUTOMATIC</option>
256
+ <option value="2">MANUAL</option>
257
+ </select>
258
+ </div>
259
+ <div class="form-row">
260
+ <label for="node-input-adaptiveLightingOptionsCustomTemperatureAdjustment"><i class="fa fa-thermometer-half"></i> Custom Temperature Adjustment</label>
261
+ <input type="number" id="node-input-adaptiveLightingOptionsCustomTemperatureAdjustment" placeholder="0">
262
+ </div>
61
263
  </div>
62
- </div>
63
- <div id="isAccessory">
64
- <div class="form-row" style="height: 34px;">
65
- <label for="node-input-accessoryId">
66
- <i class="fa fa-rocket"></i>
67
- Accessory</label>
68
- <input id="node-input-accessoryId">
264
+ </details>
265
+
266
+ <details class="nrchkb-section">
267
+ <summary><i class="fa fa-sliders"></i> Advanced Behavior</summary>
268
+ <div class="nrchkb-section-body">
269
+ <div class="form-row">
270
+ <label for="node-input-characteristicProperties"><i class="fa fa-list-alt"></i> Characteristic Properties</label>
271
+ <input type="text" id="node-input-characteristicProperties">
272
+ </div>
273
+
274
+ <div class="form-row nrchkb-checkbox-row">
275
+ <label class="nrchkb-checkbox-label" for="node-input-waitForSetupMsg">
276
+ <input type="checkbox" id="node-input-waitForSetupMsg">
277
+ <span><i class="fa fa-hourglass-half"></i> Wait for Setup message</span>
278
+ </label>
279
+ </div>
69
280
  </div>
70
- </div>
281
+ </details>
71
282
  </div>
283
+ </script>
72
284
 
73
- <div id="isLinked" style="display: none;">
74
- <div class="form-row">
75
- <label for="node-input-parentService">
76
- <i class="fa fa-cog"></i>
77
- Parent Service</label>
78
- <select id="node-input-parentService">
79
- <option value="">Choose...</option>
80
- </select>
81
- </div>
82
- </div>
285
+ <script data-help-name="homekit-service" type="text/markdown">
286
+ # HomeKit Service
83
287
 
84
- <div class="form-row">
85
- <label for="node-input-topic"><i class="fa fa-tasks"></i> Topic</label>
86
- <input type="text" id="node-input-topic" placeholder="Topic">
87
- </div>
88
- <div class="form-row">
89
- <label class="visibleDesktop">&nbsp;</label>
90
- <div style="display: inline;">
91
- <input type="checkbox" id="node-input-filter" style="display: inline-block; width: auto; vertical-align: top;">
92
- <label for="node-input-filter" style="width: 120px;">&nbsp;&nbsp;<i class="fa fa-filter"></i> Filter on Topic</label>
93
- </div>
94
- </div>
288
+ Represents one legacy HomeKit service. It receives Node-RED messages to update HomeKit characteristics and emits messages when HomeKit changes those characteristics.
95
289
 
96
- <div class="form-row">
97
- <label for="node-input-name">
98
- <i class="fa fa-tag"></i>
99
- Name</label>
100
- <input type="text" id="node-input-name" placeholder="Name">
101
- </div>
290
+ > [!WARNING]
291
+ > `homekit-service` is deprecated and kept for existing flows. Use `homekit-service2` for new work.
102
292
 
103
- <div id="camera-configuration" style="display: none;">
104
- <label>&nbsp;&nbsp;<i class="fa fa-video-camera"></i> Camera Configuration</label>
105
- <div class="form-row">
106
- <label for="node-input-cameraConfigVideoProcessor"><i class="fa fa-cog"></i> Video Processor</label>
107
- <input type="text" id="node-input-cameraConfigVideoProcessor" placeholder="ffmpeg">
108
- </div>
109
- <div class="form-row">
110
- <label for="node-input-cameraConfigSource"><i class="fa fa-tint"></i> Source</label>
111
- <input type="text" id="node-input-cameraConfigSource" placeholder="">
112
- </div>
113
- <div class="form-row">
114
- <label for="node-input-cameraConfigStillImageSource"><i class="fa fa-picture-o"></i> Still Image Source</label>
115
- <input type="text" id="node-input-cameraConfigStillImageSource" placeholder="">
116
- </div>
117
- <div class="form-row">
118
- <label for="node-input-cameraConfigMaxStreams"><i class="fa fa-tint"></i> Max Streams</label>
119
- <input type="text" id="node-input-cameraConfigMaxStreams" placeholder="">
120
- </div>
121
- <div class="form-row">
122
- <label for="node-input-cameraConfigMaxWidth"><i class="fa fa-text-width"></i> Max Width</label>
123
- <input type="text" id="node-input-cameraConfigMaxWidth" placeholder="">
124
- </div>
125
- <div class="form-row">
126
- <label for="node-input-cameraConfigMaxHeight"><i class="fa fa-text-height"></i> Max Height</label>
127
- <input type="text" id="node-input-cameraConfigMaxHeight" placeholder="">
128
- </div>
129
- <div class="form-row">
130
- <label for="node-input-cameraConfigMaxFPS"><i class="fa fa-clock-o"></i> Max FPS</label>
131
- <input type="text" id="node-input-cameraConfigMaxFPS" placeholder="">
132
- </div>
133
- <div class="form-row">
134
- <label for="node-input-cameraConfigMaxBitrate"><i class="fa fa-clock-o"></i> Max Bitrate</label>
135
- <input type="text" id="node-input-cameraConfigMaxBitrate" placeholder="">
136
- </div>
137
- <div class="form-row">
138
- <label for="node-input-cameraConfigVideoCodec"><i class="fa fa-video-camera"></i> Video Codec</label>
139
- <input type="text" id="node-input-cameraConfigVideoCodec" placeholder="">
140
- </div>
141
- <div class="form-row">
142
- <label for="node-input-cameraConfigAudioCodec"><i class="fa fa-video-camera"></i> Audio Codec</label>
143
- <input type="text" id="node-input-cameraConfigAudioCodec" placeholder="">
144
- </div>
145
- <div class="form-row">
146
- <label for="node-input-cameraConfigAudio"><i class="fa fa-headphones"></i> Audio</label>
147
- <input type="checkbox" id="node-input-cameraConfigAudio">
148
- </div>
149
- <div class="form-row">
150
- <label for="node-input-cameraConfigPacketSize"><i class="fa fa-get-pocket"></i> Packet Size</label>
151
- <input type="text" id="node-input-cameraConfigPacketSize" placeholder="">
152
- </div>
153
- <div class="form-row">
154
- <label for="node-input-cameraConfigVerticalFlip"><i class="fa fa-undo"></i> Vertical Flip</label>
155
- <input type="checkbox" id="node-input-cameraConfigVerticalFlip">
156
- </div>
157
- <div class="form-row">
158
- <label for="node-input-cameraConfigHorizontalFlip"><i class="fa fa-undo"></i> Horizontal Flip</label>
159
- <input type="checkbox" id="node-input-cameraConfigHorizontalFlip">
160
- </div>
161
- <div class="form-row">
162
- <label for="node-input-cameraConfigMapVideo"><i class="fa fa-video-camera"></i> Map Video</label>
163
- <input type="text" id="node-input-cameraConfigMapVideo" placeholder="">
164
- </div>
165
- <div class="form-row">
166
- <label for="node-input-cameraConfigMapAudio"><i class="fa fa-headphones"></i> Map Audio</label>
167
- <input type="text" id="node-input-cameraConfigMapAudio" placeholder="">
168
- </div>
169
- <div class="form-row">
170
- <label for="node-input-cameraConfigVideoFilter"><i class="fa fa-filter"></i> Video Filter</label>
171
- <input type="text" id="node-input-cameraConfigVideoFilter" placeholder="">
172
- </div>
173
- <div class="form-row">
174
- <label for="node-input-cameraConfigAdditionalCommandLine"><i class="fa fa-plus"></i> Additional Command Line</label>
175
- <input type="text" id="node-input-cameraConfigAdditionalCommandLine" placeholder="">
176
- </div>
177
- <div class="form-row">
178
- <label for="node-input-cameraConfigDebug"><i class="fa fa-bug"></i> Debug</label>
179
- <input type="checkbox" id="node-input-cameraConfigDebug">
180
- </div>
293
+ > [!IMPORTANT]
294
+ > The migration button prepares an editor-side change. Click **Done** and then **Deploy** to commit the migration.
181
295
 
182
- <div class="form-row">
183
- <label for="node-input-cameraConfigSnapshotOutput"><i class="fa fa-picture-o"></i> Snapshot output</label>
184
- <select id="node-input-cameraConfigSnapshotOutput">
185
- <option value="disabled" selected="selected">Disabled</option>
186
- <option value="path">Path</option>
187
- <option value="content">Content</option>
188
- </select>
189
- </div>
296
+ ## Message Format
190
297
 
191
- <div class="form-row">
192
- <label for="node-input-cameraConfigInterfaceName"><i class="fa fa-smile-o"></i> Interface Name</label>
193
- <input type="text" id="node-input-cameraConfigInterfaceName" placeholder="">
194
- </div>
195
- </div>
298
+ Send a `msg.payload` object whose keys are HomeKit characteristic names.
196
299
 
197
- <div id="adaptive-lightning-configuration" style="display: none; border: 1px solid var(--red-ui-secondary-border-color); padding: 12px 12px 0 12px; margin-bottom: 12px;">
198
- <label>&nbsp;&nbsp;<i class="fa fa-cog"></i> Adaptive Lightning Configuration</label>
199
- <div class="form-row">
200
- <label for="node-input-adaptiveLightingOptionsEnable"><i class="fa fa-toggle-on"></i> Enable</label>
201
- <input type="checkbox" id="node-input-adaptiveLightingOptionsEnable">
202
- </div>
203
- <div class="form-row">
204
- <label for="node-input-adaptiveLightingOptionsMode"><i class="fa fa-hand-o-up"></i> Mode</label>
205
- <select id="node-input-adaptiveLightingOptionsMode">
206
- <option value="" selected hidden disabled>AUTOMATIC</option>
207
- <option value="1">AUTOMATIC</option>
208
- <option value="2">MANUAL</option>
209
- </select>
210
- </div>
211
- <div class="form-row">
212
- <label for="node-input-adaptiveLightingOptionsCustomTemperatureAdjustment"><i class="fa fa-thermometer-quarter"></i> Custom Temperature Adjustment</label>
213
- <input type="number" id="node-input-adaptiveLightingOptionsCustomTemperatureAdjustment" placeholder="0">
214
- </div>
215
- </div>
300
+ ```json
301
+ {
302
+ "On": true,
303
+ "OutletInUse": true
304
+ }
305
+ ```
216
306
 
217
- <div class="form-row">
218
- <label for="node-input-characteristicProperties"><i class="fa fa-wrench"></i> Characteristic Properties</label>
219
- <input type="text" id="node-input-characteristicProperties" style="width: 70%">
220
- </div>
307
+ ## Placement
221
308
 
222
- <div class="form-row">
223
- <label for="node-input-waitForSetupMsg"><i class="fa fa-bug"></i> Wait for Setup message</label>
224
- <input type="checkbox" id="node-input-waitForSetupMsg">
225
- </div>
226
- </script>
309
+ - **Host Type**: Publish a parent service behind a shared **Bridge** or behind a standalone **Accessory**.
310
+ - **Bridge**: HomeKit bridge configuration that hosts this service.
311
+ - **Accessory**: Standalone accessory configuration that hosts this service.
312
+ - **Parent Service**: Parent service used by linked services.
227
313
 
228
- <script data-help-name="homekit-service" type="text/x-red">
229
- <h3 id="toc_5">Service</h3>
230
- <p>The Service node represents the single device you want to control or query.</p>
231
- <p>Every service node creates its own HAP accessory to keep things simple.</p>
232
- <ul>
233
- <li><strong>Service Hierarchy</strong>: Choose if this Service is Parent or Linked</li>
234
- <ul>
235
- <li>Parent has <strong>Bridge</strong> and <strong>Accessory Category</strong>: On what Bridge to host this Service and its Accessory and what kind of category is this Accessory, default <em>OTHER</em></li>
236
- <li>Linked has <strong>Parent Service</strong>: On what Parent Service link this Service.</li>
237
- </ul>
238
- <li><strong>Topic</strong>: An optional property that can be configured in the node or, if left blank, can be set by <code>msg.topic</code>. If <em>Filter on Topic</em> is selected <code>msg.topic</code> of incoming messages must match the configured
239
- value for the message to be accepted. If <em>Filter on Topic</em> is selected and no <em>Topic</em> is set on the node, then <code>msg.topic</code> must match the nodes <em>Name</em>
240
- </li>
241
- <li><strong>Manufacturer, Model, Serial Number</strong>: Can be anything you want.</li>
242
- <li><strong>Firmware Revision</strong>: Should be a version number string in the form of <em>MAJOR.MINOR.REVISION</em> e.g. <em>1.2.0</em>. Other types of strings are ignored and won't be displayed.</li>
243
- <li><strong>Hardware Revision</strong>: Should be a version number string in the form of <em>MAJOR.MINOR.REVISION</em> e.g. <em>1.2.0</em>. Other types of strings are ignored and won't be displayed.</li>
244
- <li><strong>Software Revision</strong>: Should be a version number string in the form of <em>MAJOR.MINOR.REVISION</em> e.g. <em>1.2.0</em>. Other types of strings are ignored and won't be displayed.</li>
245
- <li><strong>Service</strong>: Choose the type of Service from the list.</li>
246
- <li><strong>Name</strong>: <em>optional</em></li>
247
- <li><strong>Camera Configuration</strong>: Additional configuration for CameraControl service.</li>
248
- <ul>
249
- <li><strong>Video Processor</strong>: Video processor used for Camera. Default is <em>ffmpeg</em>.</li>
250
- <li><strong>Source</strong>: Camera source used for video processor. Example for ffmpeg <em>-re -i rtsp://192.168.0.227:8554/unicast</em></li>
251
- <li><strong>Still Image Source</strong>: Camera snapshot source used for video processor. Example for ffmpeg <em>-i http://faster_still_image_grab_url/this_is_optional.jpg</em></li>
252
- <li><strong>Max Streams</strong>: Maximum number of streams that will be generated for this camera, default <em>2</em>.</li>
253
- <li><strong>Max Width</strong>: Maximum width reported to HomeKit, default <em>1280</em>.</li>
254
- <li><strong>Max Height</strong>: Maximum height reported to HomeKit, default <em>720</em>.</li>
255
- <li><strong>Max FPS</strong>: Maximum frame rate of the stream, default <em>10</em>.</li>
256
- <li><strong>Max Bitrate</strong>: Maximum bit rate of the stream in kbit/s, default <em>300</em>.</li>
257
- <li><strong>Video Codec</strong>: If you're running on a RPi with the omx version of ffmpeg installed, you can change to the hardware accelerated video codec with this option, default <em>libx264</em>.</li>
258
- <li><strong>Audio Codec</strong>: If you're running on a RPi with the omx version of ffmpeg installed, you can change to the hardware accelerated audio codec with this option, default <em>libfdk_aac</em>.</li>
259
- <li><strong>Audio</strong>: Can be set to true to enable audio streaming from camera. To use audio ffmpeg must be compiled with --enable-libfdk-aac, default <em>false</em>.</li>
260
- <li><strong>Packet Size</strong>: If audio or video is choppy try a smaller value, set to a multiple of 188, default <em>1316</em>.</li>
261
- <li><strong>Vertical Flip</strong>: Flips the stream vertically, default <em>false</em>.</li>
262
- <li><strong>Horizontal Flip</strong>: Flips the stream horizontally, default <em>false</em>.</li>
263
- <li><strong>Map Video</strong>: Select the stream used for video, default <em>0:0</em>.</li>
264
- <li><strong>Map Audio</strong>: Select the stream used for audio, default <em>0:1</em>.</li>
265
- <li><strong>Video Filter</strong>: Allows a custom video filter to be passed to FFmpeg via -vf, defaults to <em>scale=1280:720</em> but is optional.</li>
266
- <li><strong>Additional Command Line</strong>: Allows additional of extra command line options to FFmpeg, default <em>-tune zerolatency</em> but is optional.</li>
267
- <li><strong>Debug</strong>: Show the output of ffmpeg in the log, default <em>false</em>.</li>
268
- <li><strong>Snapshot output</strong>: Choose how to output camera snapshot</li>
269
- <ul>
270
- <li><strong>Disabled</strong>: there will be no output</li>
271
- <li><strong>Path</strong>: file will be saved and path will be send to output, <em>msg.payload.cameraSnapshot</em> contains path value stored as a string.</li>
272
- <li><strong>Content</strong>: file content will be send to output, <em>msg.payload.cameraSnapshot</em> contains Buffer object {"type":"Buffer","data":[]}.</li>
273
- </ul>
274
- <li><strong>Interface Name</strong>: Selects the IP address of a given network interface.</li>
275
- </ul>
276
- <li><strong>Characteristic Properties</strong>: Customize the properties of characteristics.</li>
277
- <li><strong>Wait for Setup message</strong>: If yes then Service node will wait for a input message with appropriate payload.</li>
278
- </ul>
279
- <h2 id="toc_6">Input Messages</h2>
280
- <p>Input messages can be used to update any <em>Characteristic</em> that the selected <em>Service</em> provides. Simply pass the values-to-update as <code>msg.payload</code> object. </p>
281
- <p><strong>Example</strong>: to signal that an <em>Outlet</em> is turned on and in use, send the following payload</p>
282
- <div>
283
- <pre><code class="language-javascript">
284
- {
285
- &quot;On&quot;: 1,
286
- &quot;OutletInUse&quot;: 1
287
- }
288
- </code></pre>
289
- </div>
290
- <p><strong>Hint</strong>: to find out what <em>Characteristics</em> you can address, just send <code>{&quot;foo&quot;:&quot;bar&quot;}</code> and watch the debug tab ;)</p>
291
- <h2 id="toc_7">Output Messages</h2>
292
- <p>Output messages are in the same format as input messages. They are emitted from the node when it receives <em>Characteristics</em> updates from a paired iOS device.</p>
293
- <h2 id="toc_8">Characteristic Properties</h2>
294
- <p><strong>Example</strong>: allow temperatures below 0&deg;C</p>
295
- <div>
296
- <pre><code class="language-json">
297
- {
298
- &quot;CurrentTemperature&quot;: {
299
- &quot;minValue&quot;: -100
300
- }
301
- }
302
- </code></pre>
303
- <p><strong>Example</strong>: limit fan speed multiples of 25%</p>
304
- <div>
305
- <pre><code class="language-json">
306
- {
307
- &quot;RotationSpeed&quot;: {
308
- &quot;minStep&quot;: 25
309
- }
310
- }
311
- </code></pre>
312
- </div>
314
+ ## Advanced Behavior
315
+
316
+ > [!CAUTION]
317
+ > **Wait for Setup message** delays service setup until the node receives the required setup payload. The service will not be published until that message arrives.
313
318
 
319
+ - **Characteristic Properties**: JSON object that overrides HAP characteristic metadata such as `minValue`, `maxValue`, or `minStep`.
320
+ - **Camera Configuration**: Shown for camera services and describes FFmpeg streaming and snapshot settings.
314
321
  </script>
315
322
 
316
323
  <script type="text/javascript">
317
324
  RED.nodes.registerType('homekit-service', {
318
325
  category: "Apple HomeKit",
319
326
  paletteLabel: 'service',
327
+ nrchkbPreventNewInstances: true,
320
328
  defaults: {
321
329
  isParent: {
322
330
  value: 'false',
@@ -481,8 +489,7 @@
481
489
 
482
490
  try {
483
491
  JSON.parse(value)
484
- } catch (e) {
485
- console.log(e, value)
492
+ } catch (_) {
486
493
  return false
487
494
  }
488
495
 
@@ -530,6 +537,9 @@
530
537
  labelStyle: function () {
531
538
  return this.name ? 'node_label_italic' : ''
532
539
  },
540
+ onadd: function () {
541
+ applyDefaultNodeDocumentation(this, 'homekit-service')
542
+ },
533
543
  oneditprepare: function () {
534
544
  let node = this
535
545
  let isParentToggle = $('#node-config-input-isParent')
@@ -540,6 +550,151 @@
540
550
  isParentToggle.val('true')
541
551
  }
542
552
 
553
+ const toSerializableValue = function (value) {
554
+ if (value === undefined || value === null) {
555
+ return value
556
+ }
557
+
558
+ if (typeof value !== 'object') {
559
+ return value
560
+ }
561
+
562
+ try {
563
+ return JSON.parse(JSON.stringify(value))
564
+ } catch (_) {
565
+ return undefined
566
+ }
567
+ }
568
+
569
+ const readCurrentNodeConfig = function () {
570
+ const config = {
571
+ id: node.id,
572
+ type: node.type,
573
+ z: node.z,
574
+ g: node.g,
575
+ wires: toSerializableValue(node.wires),
576
+ outputs: node.outputs
577
+ }
578
+
579
+ Object.keys((node._def && node._def.defaults) || {}).forEach(function (key) {
580
+ const value = toSerializableValue(node[key])
581
+
582
+ if (value !== undefined) {
583
+ config[key] = value
584
+ }
585
+ })
586
+
587
+ return $.extend(config, {
588
+ name: $('#node-input-name').val(),
589
+ serviceName:
590
+ $('#node-input-serviceName').val() ||
591
+ $('#node-input-serviceName option:selected').attr('value') ||
592
+ node.serviceName,
593
+ manufacturer: $('#node-input-manufacturer').val(),
594
+ model: $('#node-input-model').val(),
595
+ serialNo: $('#node-input-serialNo').val(),
596
+ firmwareRev: $('#node-input-firmwareRev').val(),
597
+ cameraConfigVideoProcessor: $('#node-input-cameraConfigVideoProcessor').val(),
598
+ cameraConfigSource: $('#node-input-cameraConfigSource').val(),
599
+ cameraConfigStillImageSource: $('#node-input-cameraConfigStillImageSource').val(),
600
+ cameraConfigMaxStreams: $('#node-input-cameraConfigMaxStreams').val(),
601
+ cameraConfigMaxWidth: $('#node-input-cameraConfigMaxWidth').val(),
602
+ cameraConfigMaxHeight: $('#node-input-cameraConfigMaxHeight').val(),
603
+ cameraConfigMaxFPS: $('#node-input-cameraConfigMaxFPS').val(),
604
+ cameraConfigMaxBitrate: $('#node-input-cameraConfigMaxBitrate').val(),
605
+ cameraConfigVideoCodec: $('#node-input-cameraConfigVideoCodec').val(),
606
+ cameraConfigAudio: $('#node-input-cameraConfigAudio').is(':checked'),
607
+ cameraConfigPacketSize: $('#node-input-cameraConfigPacketSize').val(),
608
+ cameraConfigMapVideo: $('#node-input-cameraConfigMapVideo').val(),
609
+ cameraConfigMapAudio: $('#node-input-cameraConfigMapAudio').val(),
610
+ cameraConfigVideoFilter: $('#node-input-cameraConfigVideoFilter').val(),
611
+ cameraConfigAdditionalCommandLine: $('#node-input-cameraConfigAdditionalCommandLine').val(),
612
+ cameraConfigDebug: $('#node-input-cameraConfigDebug').is(':checked')
613
+ })
614
+ }
615
+
616
+ const showMigrationPrepared = function (result) {
617
+ if (
618
+ result.node.serviceName &&
619
+ $('#node-input-serviceName option[value="' + result.node.serviceName + '"]').length === 0
620
+ ) {
621
+ $('#node-input-serviceName').append(
622
+ $('<option></option>')
623
+ .val(result.node.serviceName)
624
+ .text(result.node.serviceName)
625
+ )
626
+ }
627
+
628
+ if (result.node.serviceName) {
629
+ $('#node-input-serviceName')
630
+ .val(result.node.serviceName)
631
+ .trigger('change')
632
+ }
633
+
634
+ $('#nrchkb-migration-result')
635
+ .html(
636
+ '<strong>Migration prepared.</strong> Service is now <code>' +
637
+ $('<div>').text(result.node.serviceName || '').html() +
638
+ '</code>. Click <strong>Done</strong> to commit this editor change, then <strong>Deploy</strong>.'
639
+ )
640
+ .show()
641
+ }
642
+
643
+ $('#nrchkb-migrate-service2').on('click', function () {
644
+ $('<div>')
645
+ .html('<p>This will prepare this node for conversion to <strong>homekit-service2</strong> in legacy output mode and preserve existing wires.</p><p>CameraControl nodes will become the new <strong>Camera</strong> service with the embedded Homebridge Camera FFmpeg plugin.</p><p>After migration is prepared, click <strong>Done</strong> in this node editor to commit the change, then <strong>Deploy</strong>.</p>')
646
+ .dialog({
647
+ modal: true,
648
+ resizable: false,
649
+ width: 420,
650
+ title: 'Migrate Service Node',
651
+ buttons: {
652
+ Migrate: function () {
653
+ const dialog = this
654
+
655
+ $.ajax({
656
+ url: 'nrchkb/migration/node',
657
+ type: 'POST',
658
+ contentType: 'application/json',
659
+ data: JSON.stringify(readCurrentNodeConfig())
660
+ }).done(function (result) {
661
+ if (!result || !result.migrated || !result.node) {
662
+ RED.notify('This node could not be migrated.', 'warning')
663
+ return
664
+ }
665
+
666
+ node._nrchkbPendingMigration = result.node
667
+ showMigrationPrepared(result)
668
+
669
+ RED.nodes.dirty(true)
670
+
671
+ if (RED.view && RED.view.redraw) {
672
+ RED.view.redraw(true)
673
+ }
674
+
675
+ RED.notify(
676
+ result.cameraMigrated
677
+ ? 'Migration prepared: CameraControl will become homekit-service2 Camera. Click Done, then Deploy.'
678
+ : 'Migration prepared. Click Done, then Deploy.',
679
+ 'success'
680
+ )
681
+
682
+ $(dialog).dialog('destroy')
683
+ $('.ui-dialog-content').dialog('close')
684
+ }).fail(function (xhr) {
685
+ const message = xhr.responseJSON && xhr.responseJSON.error
686
+ ? xhr.responseJSON.error
687
+ : 'Migration failed.'
688
+ RED.notify(message, 'error')
689
+ })
690
+ },
691
+ Cancel: function () {
692
+ $(this).dialog('destroy')
693
+ },
694
+ },
695
+ })
696
+ })
697
+
543
698
  let isLinkedDiv = $('#isLinked')
544
699
  let isParentDiv = $('#isParent')
545
700
 
@@ -622,6 +777,7 @@
622
777
  selectServiceName
623
778
  .change(function () {
624
779
  if (this.value === 'CameraControl') {
780
+ cameraConfiguration.prop('open', true)
625
781
  cameraConfiguration.fadeIn('fast')
626
782
  node.outputs = 3
627
783
  } else {
@@ -634,6 +790,7 @@
634
790
  selectServiceName
635
791
  .change(function () {
636
792
  if (this.value === 'Lightbulb') {
793
+ adaptiveLightningConfiguration.prop('open', true)
637
794
  adaptiveLightningConfiguration.fadeIn('fast')
638
795
  } else {
639
796
  adaptiveLightningConfiguration.fadeOut('fast')
@@ -657,6 +814,8 @@
657
814
  })
658
815
  ]
659
816
 
817
+ const parentServiceOptions = []
818
+
660
819
  candidateNodes.forEach(function (n) {
661
820
  if (!n.name || n.name.length < 1 || !n.isParent) {
662
821
  return
@@ -689,14 +848,17 @@
689
848
  const text = n.name + ' (' + sublabel + ')'
690
849
  const hostType = n.hostType
691
850
 
692
- selectParentService.append(
693
- $('<option></option>')
851
+ parentServiceOptions.push({
852
+ text,
853
+ element: $('<option></option>')
694
854
  .val(value)
695
855
  .text(text)
696
- .attr("hostType", hostType),
697
- )
856
+ .attr("hostType", hostType)
857
+ })
698
858
  })
699
859
 
860
+ sortSelectOptionsByLabel(selectParentService, parentServiceOptions)
861
+
700
862
  selectParentService
701
863
  .find('option')
702
864
  .filter(function () {
@@ -753,9 +915,27 @@
753
915
  })
754
916
  })
755
917
  },
918
+ oneditcancel: function () {
919
+ delete this._nrchkbPendingMigration
920
+ },
756
921
  oneditsave: function () {
757
922
  let node = this
758
923
 
924
+ if (node._nrchkbPendingMigration) {
925
+ const migratedNode = node._nrchkbPendingMigration
926
+
927
+ Object.keys(migratedNode).forEach(function (key) {
928
+ node[key] = migratedNode[key]
929
+ })
930
+
931
+ if (RED.nodes.getType(migratedNode.type)) {
932
+ node._def = RED.nodes.getType(migratedNode.type)
933
+ }
934
+
935
+ delete node._nrchkbPendingMigration
936
+ return true
937
+ }
938
+
759
939
  node.isParent = $('#node-config-input-isParent').val() === 'true'
760
940
  node.hostType = $('#node-config-input-hostType').val()
761
941
 
@@ -771,5 +951,43 @@
771
951
  ).val()
772
952
  },
773
953
  })
774
- </script>
775
954
 
955
+ ;(function () {
956
+ const legacyServiceType = 'homekit-service'
957
+ const registry = RED.nodes.registry
958
+ const legacyServiceDefinition = RED.nodes.getType(legacyServiceType)
959
+
960
+ if (legacyServiceDefinition) {
961
+ legacyServiceDefinition.nrchkbPreventNewInstances = true
962
+ }
963
+
964
+ if (registry && !registry._nrchkbPreventNewInstancesPatch) {
965
+ const originalGetNodeTypes = registry.getNodeTypes
966
+
967
+ registry.getNodeTypes = function () {
968
+ return originalGetNodeTypes.call(registry).filter(function (type) {
969
+ const definition = RED.nodes.getType(type)
970
+ return !definition || definition.nrchkbPreventNewInstances !== true
971
+ })
972
+ }
973
+
974
+ registry._nrchkbPreventNewInstancesPatch = true
975
+ }
976
+
977
+ const removeLegacyPaletteNode = function () {
978
+ $('.red-ui-palette-node[data-palette-type="' + legacyServiceType + '"]').remove()
979
+ }
980
+
981
+ if (registry && RED.events && RED.events.on && !registry._nrchkbLegacyServicePaletteHandler) {
982
+ RED.events.on('registry:node-type-added', function (type) {
983
+ if (type === legacyServiceType) {
984
+ removeLegacyPaletteNode()
985
+ }
986
+ })
987
+ registry._nrchkbLegacyServicePaletteHandler = true
988
+ }
989
+
990
+ removeLegacyPaletteNode()
991
+ setTimeout(removeLegacyPaletteNode, 0)
992
+ })()
993
+ </script>