node-red-contrib-homekit-bridged 2.0.0-dev.5 → 2.0.0-dev.8

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 +152 -90
  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 +433 -375
  45. package/build/lib/utils/ServiceUtils2.js +519 -309
  46. package/build/lib/utils/index.js +10 -11
  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 +544 -307
  55. package/build/nodes/service.js +5 -6
  56. package/build/nodes/service2.html +1721 -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 -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">
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>
37
10
  </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">
41
- </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,9 +537,31 @@
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')
546
+ const prefersReducedMotion = function () {
547
+ return window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches
548
+ }
549
+ const showEditorSection = function (section) {
550
+ if (prefersReducedMotion()) {
551
+ section.stop(true, true).show()
552
+ return
553
+ }
554
+
555
+ section.stop(true, true).fadeIn('fast')
556
+ }
557
+ const hideEditorSection = function (section) {
558
+ if (prefersReducedMotion()) {
559
+ section.stop(true, true).hide()
560
+ return
561
+ }
562
+
563
+ section.stop(true, true).fadeOut('fast')
564
+ }
536
565
 
537
566
  if (node.isParent === false) {
538
567
  isParentToggle.val('false')
@@ -540,6 +569,151 @@
540
569
  isParentToggle.val('true')
541
570
  }
542
571
 
572
+ const toSerializableValue = function (value) {
573
+ if (value === undefined || value === null) {
574
+ return value
575
+ }
576
+
577
+ if (typeof value !== 'object') {
578
+ return value
579
+ }
580
+
581
+ try {
582
+ return JSON.parse(JSON.stringify(value))
583
+ } catch (_) {
584
+ return undefined
585
+ }
586
+ }
587
+
588
+ const readCurrentNodeConfig = function () {
589
+ const config = {
590
+ id: node.id,
591
+ type: node.type,
592
+ z: node.z,
593
+ g: node.g,
594
+ wires: toSerializableValue(node.wires),
595
+ outputs: node.outputs
596
+ }
597
+
598
+ Object.keys((node._def && node._def.defaults) || {}).forEach(function (key) {
599
+ const value = toSerializableValue(node[key])
600
+
601
+ if (value !== undefined) {
602
+ config[key] = value
603
+ }
604
+ })
605
+
606
+ return $.extend(config, {
607
+ name: $('#node-input-name').val(),
608
+ serviceName:
609
+ $('#node-input-serviceName').val() ||
610
+ $('#node-input-serviceName option:selected').attr('value') ||
611
+ node.serviceName,
612
+ manufacturer: $('#node-input-manufacturer').val(),
613
+ model: $('#node-input-model').val(),
614
+ serialNo: $('#node-input-serialNo').val(),
615
+ firmwareRev: $('#node-input-firmwareRev').val(),
616
+ cameraConfigVideoProcessor: $('#node-input-cameraConfigVideoProcessor').val(),
617
+ cameraConfigSource: $('#node-input-cameraConfigSource').val(),
618
+ cameraConfigStillImageSource: $('#node-input-cameraConfigStillImageSource').val(),
619
+ cameraConfigMaxStreams: $('#node-input-cameraConfigMaxStreams').val(),
620
+ cameraConfigMaxWidth: $('#node-input-cameraConfigMaxWidth').val(),
621
+ cameraConfigMaxHeight: $('#node-input-cameraConfigMaxHeight').val(),
622
+ cameraConfigMaxFPS: $('#node-input-cameraConfigMaxFPS').val(),
623
+ cameraConfigMaxBitrate: $('#node-input-cameraConfigMaxBitrate').val(),
624
+ cameraConfigVideoCodec: $('#node-input-cameraConfigVideoCodec').val(),
625
+ cameraConfigAudio: $('#node-input-cameraConfigAudio').is(':checked'),
626
+ cameraConfigPacketSize: $('#node-input-cameraConfigPacketSize').val(),
627
+ cameraConfigMapVideo: $('#node-input-cameraConfigMapVideo').val(),
628
+ cameraConfigMapAudio: $('#node-input-cameraConfigMapAudio').val(),
629
+ cameraConfigVideoFilter: $('#node-input-cameraConfigVideoFilter').val(),
630
+ cameraConfigAdditionalCommandLine: $('#node-input-cameraConfigAdditionalCommandLine').val(),
631
+ cameraConfigDebug: $('#node-input-cameraConfigDebug').is(':checked')
632
+ })
633
+ }
634
+
635
+ const showMigrationPrepared = function (result) {
636
+ if (
637
+ result.node.serviceName &&
638
+ $('#node-input-serviceName option[value="' + result.node.serviceName + '"]').length === 0
639
+ ) {
640
+ $('#node-input-serviceName').append(
641
+ $('<option></option>')
642
+ .val(result.node.serviceName)
643
+ .text(result.node.serviceName)
644
+ )
645
+ }
646
+
647
+ if (result.node.serviceName) {
648
+ $('#node-input-serviceName')
649
+ .val(result.node.serviceName)
650
+ .trigger('change')
651
+ }
652
+
653
+ $('#nrchkb-migration-result')
654
+ .html(
655
+ '<strong>Migration prepared.</strong> Service is now <code>' +
656
+ $('<div>').text(result.node.serviceName || '').html() +
657
+ '</code>. Click <strong>Done</strong> to commit this editor change, then <strong>Deploy</strong>.'
658
+ )
659
+ .show()
660
+ }
661
+
662
+ $('#nrchkb-migrate-service2').on('click', function () {
663
+ $('<div>')
664
+ .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>')
665
+ .dialog({
666
+ modal: true,
667
+ resizable: false,
668
+ width: 420,
669
+ title: 'Migrate Service Node',
670
+ buttons: {
671
+ Migrate: function () {
672
+ const dialog = this
673
+
674
+ $.ajax({
675
+ url: 'nrchkb/migration/node',
676
+ type: 'POST',
677
+ contentType: 'application/json',
678
+ data: JSON.stringify(readCurrentNodeConfig())
679
+ }).done(function (result) {
680
+ if (!result || !result.migrated || !result.node) {
681
+ RED.notify('This node could not be migrated.', 'warning')
682
+ return
683
+ }
684
+
685
+ node._nrchkbPendingMigration = result.node
686
+ showMigrationPrepared(result)
687
+
688
+ RED.nodes.dirty(true)
689
+
690
+ if (RED.view && RED.view.redraw) {
691
+ RED.view.redraw(true)
692
+ }
693
+
694
+ RED.notify(
695
+ result.cameraMigrated
696
+ ? 'Migration prepared: CameraControl will become homekit-service2 Camera. Click Done, then Deploy.'
697
+ : 'Migration prepared. Click Done, then Deploy.',
698
+ 'success'
699
+ )
700
+
701
+ $(dialog).dialog('destroy')
702
+ $('.ui-dialog-content').dialog('close')
703
+ }).fail(function (xhr) {
704
+ const message = xhr.responseJSON && xhr.responseJSON.error
705
+ ? xhr.responseJSON.error
706
+ : 'Migration failed.'
707
+ RED.notify(message, 'error')
708
+ })
709
+ },
710
+ Cancel: function () {
711
+ $(this).dialog('destroy')
712
+ },
713
+ },
714
+ })
715
+ })
716
+
543
717
  let isLinkedDiv = $('#isLinked')
544
718
  let isParentDiv = $('#isParent')
545
719
 
@@ -548,11 +722,11 @@
548
722
  isParentToggle
549
723
  .change(function () {
550
724
  if (isParentToggle.val() === 'true') {
551
- isLinkedDiv.fadeOut('fast')
552
- isParentDiv.fadeIn('fast')
725
+ hideEditorSection(isLinkedDiv)
726
+ showEditorSection(isParentDiv)
553
727
  } else {
554
- isParentDiv.fadeOut('fast')
555
- isLinkedDiv.fadeIn('fast')
728
+ hideEditorSection(isParentDiv)
729
+ showEditorSection(isLinkedDiv)
556
730
  selectHostType.val("_ADD_")
557
731
  }
558
732
  })
@@ -622,10 +796,11 @@
622
796
  selectServiceName
623
797
  .change(function () {
624
798
  if (this.value === 'CameraControl') {
625
- cameraConfiguration.fadeIn('fast')
799
+ cameraConfiguration.prop('open', true)
800
+ showEditorSection(cameraConfiguration)
626
801
  node.outputs = 3
627
802
  } else {
628
- cameraConfiguration.fadeOut('fast')
803
+ hideEditorSection(cameraConfiguration)
629
804
  node.outputs = 2
630
805
  }
631
806
  })
@@ -634,9 +809,10 @@
634
809
  selectServiceName
635
810
  .change(function () {
636
811
  if (this.value === 'Lightbulb') {
637
- adaptiveLightningConfiguration.fadeIn('fast')
812
+ adaptiveLightningConfiguration.prop('open', true)
813
+ showEditorSection(adaptiveLightningConfiguration)
638
814
  } else {
639
- adaptiveLightningConfiguration.fadeOut('fast')
815
+ hideEditorSection(adaptiveLightningConfiguration)
640
816
  }
641
817
  })
642
818
  .change()
@@ -657,6 +833,8 @@
657
833
  })
658
834
  ]
659
835
 
836
+ const parentServiceOptions = []
837
+
660
838
  candidateNodes.forEach(function (n) {
661
839
  if (!n.name || n.name.length < 1 || !n.isParent) {
662
840
  return
@@ -689,14 +867,17 @@
689
867
  const text = n.name + ' (' + sublabel + ')'
690
868
  const hostType = n.hostType
691
869
 
692
- selectParentService.append(
693
- $('<option></option>')
870
+ parentServiceOptions.push({
871
+ text,
872
+ element: $('<option></option>')
694
873
  .val(value)
695
874
  .text(text)
696
- .attr("hostType", hostType),
697
- )
875
+ .attr("hostType", hostType)
876
+ })
698
877
  })
699
878
 
879
+ sortSelectOptionsByLabel(selectParentService, parentServiceOptions)
880
+
700
881
  selectParentService
701
882
  .find('option')
702
883
  .filter(function () {
@@ -753,9 +934,27 @@
753
934
  })
754
935
  })
755
936
  },
937
+ oneditcancel: function () {
938
+ delete this._nrchkbPendingMigration
939
+ },
756
940
  oneditsave: function () {
757
941
  let node = this
758
942
 
943
+ if (node._nrchkbPendingMigration) {
944
+ const migratedNode = node._nrchkbPendingMigration
945
+
946
+ Object.keys(migratedNode).forEach(function (key) {
947
+ node[key] = migratedNode[key]
948
+ })
949
+
950
+ if (RED.nodes.getType(migratedNode.type)) {
951
+ node._def = RED.nodes.getType(migratedNode.type)
952
+ }
953
+
954
+ delete node._nrchkbPendingMigration
955
+ return true
956
+ }
957
+
759
958
  node.isParent = $('#node-config-input-isParent').val() === 'true'
760
959
  node.hostType = $('#node-config-input-hostType').val()
761
960
 
@@ -771,5 +970,43 @@
771
970
  ).val()
772
971
  },
773
972
  })
774
- </script>
775
973
 
974
+ ;(function () {
975
+ const legacyServiceType = 'homekit-service'
976
+ const registry = RED.nodes.registry
977
+ const legacyServiceDefinition = RED.nodes.getType(legacyServiceType)
978
+
979
+ if (legacyServiceDefinition) {
980
+ legacyServiceDefinition.nrchkbPreventNewInstances = true
981
+ }
982
+
983
+ if (registry && !registry._nrchkbPreventNewInstancesPatch) {
984
+ const originalGetNodeTypes = registry.getNodeTypes
985
+
986
+ registry.getNodeTypes = function () {
987
+ return originalGetNodeTypes.call(registry).filter(function (type) {
988
+ const definition = RED.nodes.getType(type)
989
+ return !definition || definition.nrchkbPreventNewInstances !== true
990
+ })
991
+ }
992
+
993
+ registry._nrchkbPreventNewInstancesPatch = true
994
+ }
995
+
996
+ const removeLegacyPaletteNode = function () {
997
+ $('.red-ui-palette-node[data-palette-type="' + legacyServiceType + '"]').remove()
998
+ }
999
+
1000
+ if (registry && RED.events && RED.events.on && !registry._nrchkbLegacyServicePaletteHandler) {
1001
+ RED.events.on('registry:node-type-added', function (type) {
1002
+ if (type === legacyServiceType) {
1003
+ removeLegacyPaletteNode()
1004
+ }
1005
+ })
1006
+ registry._nrchkbLegacyServicePaletteHandler = true
1007
+ }
1008
+
1009
+ removeLegacyPaletteNode()
1010
+ setTimeout(removeLegacyPaletteNode, 0)
1011
+ })()
1012
+ </script>