node-red-contrib-homekit-bridged 2.0.0-dev.7 → 2.0.0-dev.9

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.
@@ -136,18 +136,15 @@ class Storage {
136
136
  static loadCustomCharacteristics() {
137
137
  return Storage.load(import_StorageType.StorageType.CUSTOM_CHARACTERISTICS);
138
138
  }
139
- static loadService(key) {
140
- return new Promise((resolve, reject) => {
141
- Storage.load(import_StorageType.StorageType.SERVICE, key).then((value) => {
142
- if (value === void 0) {
143
- reject("Service data not exists");
144
- } else if ("primaryService" in value) {
145
- resolve(value);
146
- } else {
147
- reject("Service data corrupted");
148
- }
149
- });
150
- });
139
+ static async loadService(key) {
140
+ const value = await Storage.load(import_StorageType.StorageType.SERVICE, key);
141
+ if (value === void 0) {
142
+ throw "Service data not exists";
143
+ }
144
+ if (typeof value === "object" && value !== null && "primaryService" in value) {
145
+ return value;
146
+ }
147
+ throw "Service data corrupted";
151
148
  }
152
149
  static loadAccessory(key) {
153
150
  return Storage.load(import_StorageType.StorageType.ACCESSORY, key);
@@ -380,7 +380,6 @@ const buildServiceUtils = (node) => {
380
380
  node.removeListener("input", node.handleWaitForSetup);
381
381
  resolve(newConfig);
382
382
  } else {
383
- node.removeListener("input", node.handleWaitForSetup);
384
383
  log.error(
385
384
  'Invalid message (required {"payload":{"nrchkb":{"setup":{}}}})'
386
385
  );
@@ -47,7 +47,13 @@ const describeContext = (context) => {
47
47
  return keys.length > 0 ? `Object(${keys.slice(0, 5).join(",")}${keys.length > 5 ? ",..." : ""})` : "Object";
48
48
  };
49
49
  const describeSupported = (supported) => Array.from(supported).join("', '");
50
- const isPluginEntry = (value) => typeof value === "object" && value !== null && typeof value.id === "string" && value.id.trim().length > 0;
50
+ const isPluginEntry = (value) => {
51
+ if (typeof value !== "object" || value === null) {
52
+ return false;
53
+ }
54
+ const id = value.id;
55
+ return typeof id === "string" && id.trim().length > 0;
56
+ };
51
57
  const parsePluginEntries = (value, log) => {
52
58
  if (!value) {
53
59
  return [];
@@ -375,7 +381,7 @@ const buildServiceUtils2 = (node) => {
375
381
  config: serviceInformation.config
376
382
  }
377
383
  });
378
- let pluginService;
384
+ let pluginService = void 0;
379
385
  const pluginSlots = [
380
386
  serviceInformation.config.plugin1,
381
387
  serviceInformation.config.plugin2,
@@ -506,7 +512,6 @@ const buildServiceUtils2 = (node) => {
506
512
  node.removeListener("input", node.handleWaitForSetup);
507
513
  resolve(newConfig);
508
514
  } else {
509
- node.removeListener("input", node.handleWaitForSetup);
510
515
  log.error(
511
516
  'Invalid message (required {"payload":{"nrchkb":{"setup":{}}}})'
512
517
  );
@@ -9,7 +9,7 @@
9
9
  </div>
10
10
  <div class="form-row">
11
11
  <label for="node-config-input-pinCode"><i class="fa fa-key"></i> Pin Code</label>
12
- <input type="text" id="node-config-input-pinCode" placeholder="xxxx-xxxx">
12
+ <input type="text" id="node-config-input-pinCode" placeholder="xxxx-xxxx" inputmode="numeric" autocomplete="off" spellcheck="false">
13
13
  </div>
14
14
  <details class="nrchkb-section">
15
15
  <summary><i class="fa fa-qrcode"></i> <span data-i18n="node-red-contrib-homekit-bridged/qr.sectionTitle">Pairing QR Code</span></summary>
@@ -31,7 +31,7 @@
31
31
  <div class="nrchkb-section-body">
32
32
  <div class="form-row">
33
33
  <label for="node-config-input-port"><i class="fa fa-plug"></i> Port</label>
34
- <input type="text" id="node-config-input-port" placeholder="Leave blank to auto assign">
34
+ <input type="text" id="node-config-input-port" placeholder="Leave blank to auto assign" inputmode="numeric" autocomplete="off" spellcheck="false">
35
35
  </div>
36
36
  <div class="form-row">
37
37
  <label for="node-config-input-advertiser"><i class="fa fa-bullhorn"></i> Advertiser</label>
@@ -44,7 +44,7 @@
44
44
  </div>
45
45
  <div class="form-row">
46
46
  <label for="node-config-input-bind"><i class="fa fa-sitemap"></i> Bind</label>
47
- <input type="text" id="node-config-input-bind" placeholder="::">
47
+ <input type="text" id="node-config-input-bind" placeholder="::" autocomplete="off" spellcheck="false">
48
48
  <input type="hidden" id="node-config-input-bindType">
49
49
  </div>
50
50
  </div>
@@ -298,6 +298,26 @@ Advertiser guide:
298
298
  applyDefaultNodeDocumentation(this, 'homekit-bridge')
299
299
  },
300
300
  oneditprepare: function () {
301
+ const prefersReducedMotion = function () {
302
+ return window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches
303
+ }
304
+ const showEditorSection = function (section) {
305
+ if (prefersReducedMotion()) {
306
+ section.stop(true, true).show()
307
+ return
308
+ }
309
+
310
+ section.stop(true, true).fadeIn('fast')
311
+ }
312
+ const hideEditorSection = function (section) {
313
+ if (prefersReducedMotion()) {
314
+ section.stop(true, true).hide()
315
+ return
316
+ }
317
+
318
+ section.stop(true, true).fadeOut('fast')
319
+ }
320
+
301
321
  if (!validatePinCode(this.pinCode)) {
302
322
  this.pinCode = generatePinCode()
303
323
  $('#node-config-input-pinCode').val(this.pinCode)
@@ -321,9 +341,9 @@ Advertiser guide:
321
341
  .change(function () {
322
342
  if (this.checked) {
323
343
  mdnsConfigurationSection.prop('open', true)
324
- mdnsConfiguration.fadeIn('fast')
344
+ showEditorSection(mdnsConfiguration)
325
345
  } else {
326
- mdnsConfiguration.fadeOut('fast')
346
+ hideEditorSection(mdnsConfiguration)
327
347
  }
328
348
  })
329
349
  .change()
@@ -364,12 +364,23 @@
364
364
  line-height: 16px;
365
365
  }
366
366
 
367
+ .nrchkb-sidebar-pairing label.nrchkb-sidebar-setting.nrchkb-checkbox-label {
368
+ display: inline-flex !important;
369
+ gap: 8px;
370
+ line-height: 1.35;
371
+ }
372
+
367
373
  .nrchkb-sidebar-pairing label.nrchkb-sidebar-setting input {
368
374
  width: 16px;
369
375
  height: 16px;
370
376
  margin: 0 !important;
371
377
  }
372
378
 
379
+ .nrchkb-sidebar-pairing label.nrchkb-sidebar-setting.nrchkb-checkbox-label input[type="checkbox"] {
380
+ width: 36px;
381
+ height: 22px;
382
+ }
383
+
373
384
  .nrchkb-sidebar-pairing label.nrchkb-sidebar-setting span {
374
385
  display: block;
375
386
  min-width: 0;
@@ -615,54 +626,34 @@
615
626
  background: var(--nrchkb-accent);
616
627
  }
617
628
 
618
- @container (max-width: 520px) {
619
- .nrchkb-editor .form-row {
620
- align-items: stretch;
621
- flex-direction: column;
622
- gap: 4px;
623
- }
624
-
625
- .nrchkb-editor .form-row > label:first-child {
626
- flex: 0 0 auto;
627
- max-width: 100%;
628
- min-width: 0;
629
- width: auto;
630
- }
631
-
632
- .nrchkb-editor .form-row > input[type="text"],
633
- .nrchkb-editor .form-row > input[type="number"],
634
- .nrchkb-editor .form-row > input:not([type]),
635
- .nrchkb-editor .form-row > select,
636
- .nrchkb-editor .form-row > .red-ui-typedInput-container,
637
- .nrchkb-editor .form-row > div[style*="inline-flex"] {
638
- flex: 0 0 auto;
639
- width: 100% !important;
629
+ @supports not (color: color-mix(in srgb, #000 50%, #fff)) {
630
+ .nrchkb-editor {
631
+ --nrchkb-toggle-off: var(--red-ui-tertiary-background, #f6f6f6);
632
+ --nrchkb-toggle-border: var(--red-ui-secondary-border-color, #d8d8d8);
640
633
  }
641
634
 
642
- .nrchkb-editor .nrchkb-checkbox-row {
643
- display: block;
635
+ .nrchkb-editor .nrchkb-section,
636
+ .nrchkb-editor .nrchkb-nested-panel,
637
+ .nrchkb-editor .nrchkb-section > summary,
638
+ .nrchkb-editor .nrchkb-advertiser-recommendation,
639
+ .nrchkb-help .nrchkb-advertiser-dynamic {
640
+ background: var(--red-ui-secondary-background, #fff);
644
641
  }
645
642
 
646
- .nrchkb-editor .nrchkb-characteristic-row {
647
- align-items: stretch;
648
- flex-direction: column;
649
- gap: 4px;
643
+ .nrchkb-editor input[type="checkbox"]:checked {
644
+ border-color: #1f8f3d;
650
645
  }
651
646
 
652
- .nrchkb-editor .nrchkb-characteristic-label {
653
- flex: 0 0 auto;
654
- max-width: 100%;
655
- min-width: 0;
656
- padding-left: 0;
647
+ .nrchkb-editor .nrchkb-checkbox-label i {
648
+ color: var(--nrchkb-muted-text);
657
649
  }
658
650
 
659
- .nrchkb-editor .nrchkb-pairing-card {
660
- align-items: flex-start;
661
- flex-direction: column;
651
+ .nrchkb-editor .nrchkb-checkbox-label input[type="checkbox"]:checked + span i {
652
+ color: #1f6f35;
662
653
  }
663
654
  }
664
655
 
665
- @media (max-width: 520px) {
656
+ @container (max-width: 520px) {
666
657
  .nrchkb-editor .form-row {
667
658
  align-items: stretch;
668
659
  flex-direction: column;
@@ -709,6 +700,55 @@
709
700
  }
710
701
  }
711
702
 
703
+ @supports not (container-type: inline-size) {
704
+ @media (max-width: 520px) {
705
+ .nrchkb-editor .form-row {
706
+ align-items: stretch;
707
+ flex-direction: column;
708
+ gap: 4px;
709
+ }
710
+
711
+ .nrchkb-editor .form-row > label:first-child {
712
+ flex: 0 0 auto;
713
+ max-width: 100%;
714
+ min-width: 0;
715
+ width: auto;
716
+ }
717
+
718
+ .nrchkb-editor .form-row > input[type="text"],
719
+ .nrchkb-editor .form-row > input[type="number"],
720
+ .nrchkb-editor .form-row > input:not([type]),
721
+ .nrchkb-editor .form-row > select,
722
+ .nrchkb-editor .form-row > .red-ui-typedInput-container,
723
+ .nrchkb-editor .form-row > div[style*="inline-flex"] {
724
+ flex: 0 0 auto;
725
+ width: 100% !important;
726
+ }
727
+
728
+ .nrchkb-editor .nrchkb-checkbox-row {
729
+ display: block;
730
+ }
731
+
732
+ .nrchkb-editor .nrchkb-characteristic-row {
733
+ align-items: stretch;
734
+ flex-direction: column;
735
+ gap: 4px;
736
+ }
737
+
738
+ .nrchkb-editor .nrchkb-characteristic-label {
739
+ flex: 0 0 auto;
740
+ max-width: 100%;
741
+ min-width: 0;
742
+ padding-left: 0;
743
+ }
744
+
745
+ .nrchkb-editor .nrchkb-pairing-card {
746
+ align-items: flex-start;
747
+ flex-direction: column;
748
+ }
749
+ }
750
+ }
751
+
712
752
  .nrchkb-editor .alert {
713
753
  position: relative;
714
754
  padding: .75rem 1.25rem;
@@ -734,17 +774,7 @@
734
774
  </style>
735
775
 
736
776
  <script type="text/javascript">
737
- const initNRCHKBConfigNode = function () {
738
- //NRCHKB Custom Characteristics
739
- $.ajax({
740
- url: 'nrchkb/config',
741
- dataType: 'json',
742
- async: false,
743
- success: function (data) {
744
- nrchkbConfig = data
745
- },
746
- })
747
-
777
+ const registerNRCHKBConfigNode = function () {
748
778
  RED.nodes.registerType('nrchkb', {
749
779
  category: 'Apple HomeKit',
750
780
  icon: 'nrchkb.png',
@@ -788,6 +818,10 @@
788
818
  const row1 = $('<div/>', {class: 'nrchkb-characteristic-row'}).appendTo(fragment)
789
819
  const row2 = $('<div/>', {class: 'nrchkb-characteristic-row'}).appendTo(fragment)
790
820
  const row3 = $('<div/>', {class: 'nrchkb-characteristic-fields'}).appendTo(fragment)
821
+ const fieldIdPrefix = 'node-input-customCharacteristic-' + i + '-'
822
+ const createFieldId = function (name) {
823
+ return fieldIdPrefix + name
824
+ }
791
825
  const setIconLabelText = function (target, iconClass, text) {
792
826
  target.empty()
793
827
  $('<i/>', {class: 'fa ' + iconClass}).appendTo(target)
@@ -799,23 +833,27 @@
799
833
  return label
800
834
  }
801
835
 
802
- const uuidLabel = $('<div/>', {
836
+ const uuidFieldId = createFieldId('uuid')
837
+ const uuidLabel = $('<label/>', {
803
838
  class: 'nrchkb-characteristic-label',
839
+ for: uuidFieldId,
804
840
  required: 'required'
805
841
  })
806
842
  .appendTo(row1)
807
843
  setIconLabelText(uuidLabel, 'fa-key', 'UUID')
808
- $('<input/>', {class: 'property-uuid', type: 'text'})
844
+ $('<input/>', {class: 'property-uuid', id: uuidFieldId, type: 'text'})
809
845
  .val(UUID ? UUID : uuidv4)
810
846
  .appendTo(row1)
811
847
 
812
- const nameLabel = $('<div/>', {
848
+ const nameFieldId = createFieldId('name')
849
+ const nameLabel = $('<label/>', {
813
850
  class: 'nrchkb-characteristic-label',
851
+ for: nameFieldId,
814
852
  required: 'required'
815
853
  })
816
854
  .appendTo(row2)
817
855
  setIconLabelText(nameLabel, 'fa-tag', 'Name')
818
- $('<input/>', {class: 'property-name', type: 'text'})
856
+ $('<input/>', {class: 'property-name', id: nameFieldId, type: 'text'})
819
857
  .val(name)
820
858
  .appendTo(row2)
821
859
 
@@ -829,12 +867,14 @@
829
867
  const row3_properties = $('<div/>', {class: 'properties'}).appendTo(row3_properties_accordion)
830
868
 
831
869
  const formatRow = $('<div/>', {class: 'form-row'}).appendTo(row3_properties)
870
+ const formatFieldId = createFieldId('format')
832
871
  createPropertyLabel({
833
872
  class: 'nrchkb-characteristic-property-label',
834
- htmlFor: 'property-format'
873
+ for: formatFieldId
835
874
  }, 'fa-code', 'Format *').appendTo(formatRow)
836
875
  const formatInput = $('<select/>', {
837
876
  class: 'property-format',
877
+ id: formatFieldId,
838
878
  required: 'required'
839
879
  }).appendTo(formatRow)
840
880
  $('<option/>').val(undefined).text('Choose...').appendTo(formatInput)
@@ -853,11 +893,12 @@
853
893
  formatInput.val(props.format)
854
894
 
855
895
  const unitRow = $('<div/>', {class: 'form-row'}).appendTo(row3_properties)
896
+ const unitFieldId = createFieldId('unit')
856
897
  createPropertyLabel({
857
898
  class: 'nrchkb-characteristic-property-label',
858
- htmlFor: 'property-unit'
899
+ for: unitFieldId
859
900
  }, 'fa-balance-scale', 'Unit').appendTo(unitRow)
860
- const unitSelect = $('<select/>', {class: 'property-unit'}).appendTo(unitRow)
901
+ const unitSelect = $('<select/>', {class: 'property-unit', id: unitFieldId}).appendTo(unitRow)
861
902
  $('<option/>').val(undefined).text('Choose...').appendTo(unitSelect)
862
903
  $('<option/>').val('celsius').text('CELSIUS').appendTo(unitSelect)
863
904
  $('<option/>').val('percentage').text('PERCENTAGE').appendTo(unitSelect)
@@ -867,12 +908,14 @@
867
908
  unitSelect.val(props.unit)
868
909
 
869
910
  const permsRow = $('<div/>', {class: 'form-row'}).appendTo(row3_properties)
911
+ const permsFieldId = createFieldId('perms')
870
912
  createPropertyLabel({
871
913
  class: 'nrchkb-characteristic-property-label',
872
- htmlFor: 'property-perms'
914
+ for: permsFieldId
873
915
  }, 'fa-lock', 'Permissions').appendTo(permsRow)
874
916
  const permsSelect = $('<select/>', {
875
917
  class: 'property-perms',
918
+ id: permsFieldId,
876
919
  multiple: 'multiple'
877
920
  }).appendTo(permsRow)
878
921
  $('<option/>').val('pr').text('PAIRED_READ / READ').appendTo(permsSelect)
@@ -885,93 +928,113 @@
885
928
  permsSelect.val(props.perms)
886
929
 
887
930
  const evRow = $('<div/>', {class: 'form-row'}).appendTo(row3_properties)
931
+ const evFieldId = createFieldId('ev')
888
932
  createPropertyLabel({
889
933
  class: 'nrchkb-characteristic-property-label',
890
- htmlFor: 'property-ev'
934
+ for: evFieldId
891
935
  }, 'fa-bell-o', 'Event Notifications').appendTo(evRow)
892
936
  $('<input/>', {
893
937
  class: 'property-ev',
938
+ id: evFieldId,
894
939
  type: 'checkbox',
895
940
  checked: props.ev
896
941
  }).appendTo(evRow)
897
942
 
898
943
  const descriptionRow = $('<div/>', {class: 'form-row'}).appendTo(row3_properties)
944
+ const descriptionFieldId = createFieldId('description')
899
945
  createPropertyLabel({
900
946
  class: 'nrchkb-characteristic-property-label',
901
- htmlFor: 'property-description'
947
+ for: descriptionFieldId
902
948
  }, 'fa-align-left', 'Description').appendTo(descriptionRow)
903
949
  $('<input/>', {
904
950
  class: 'property-description',
951
+ id: descriptionFieldId,
905
952
  type: 'text'
906
953
  }).appendTo(descriptionRow).val(props.description)
907
954
 
908
955
  const minValueRow = $('<div/>', {class: 'form-row'}).appendTo(row3_properties)
956
+ const minValueFieldId = createFieldId('minValue')
909
957
  createPropertyLabel({
910
958
  class: 'nrchkb-characteristic-property-label',
911
- htmlFor: 'property-minValue'
959
+ for: minValueFieldId
912
960
  }, 'fa-sort-numeric-asc', 'Minimum Value').appendTo(minValueRow)
913
961
  $('<input/>', {
914
962
  class: 'property-minValue',
963
+ id: minValueFieldId,
915
964
  type: 'number'
916
965
  }).appendTo(minValueRow).val(props.minValue)
917
966
 
918
967
  const maxValueRow = $('<div/>', {class: 'form-row'}).appendTo(row3_properties)
968
+ const maxValueFieldId = createFieldId('maxValue')
919
969
  createPropertyLabel({
920
970
  class: 'nrchkb-characteristic-property-label',
921
- htmlFor: 'property-maxValue'
971
+ for: maxValueFieldId
922
972
  }, 'fa-sort-numeric-desc', 'Maximum Value').appendTo(maxValueRow)
923
973
  $('<input/>', {
924
974
  class: 'property-maxValue',
975
+ id: maxValueFieldId,
925
976
  type: 'number'
926
977
  }).appendTo(maxValueRow).val(props.maxValue)
927
978
 
928
979
  const minStepRow = $('<div/>', {class: 'form-row'}).appendTo(row3_properties)
980
+ const minStepFieldId = createFieldId('minStep')
929
981
  createPropertyLabel({
930
982
  class: 'nrchkb-characteristic-property-label',
931
- htmlFor: 'property-minStep'
983
+ for: minStepFieldId
932
984
  }, 'fa-step-forward', 'Minimum Step').appendTo(minStepRow)
933
985
  $('<input/>', {
934
986
  class: 'property-minStep',
987
+ id: minStepFieldId,
935
988
  type: 'number'
936
989
  }).appendTo(minStepRow).val(props.minStep)
937
990
 
938
991
  const maxLenRow = $('<div/>', {class: 'form-row'}).appendTo(row3_properties)
992
+ const maxLenFieldId = createFieldId('maxLen')
939
993
  createPropertyLabel({
940
994
  class: 'nrchkb-characteristic-property-label',
941
- htmlFor: 'property-maxLen'
995
+ for: maxLenFieldId
942
996
  }, 'fa-text-width', 'Maximum Length').appendTo(maxLenRow)
943
997
  $('<input/>', {
944
998
  class: 'property-maxLen',
999
+ id: maxLenFieldId,
945
1000
  type: 'number'
946
1001
  }).appendTo(maxLenRow).val(props.maxLen)
947
1002
 
948
1003
  const maxDataLenRow = $('<div/>', {class: 'form-row'}).appendTo(row3_properties)
1004
+ const maxDataLenFieldId = createFieldId('maxDataLen')
949
1005
  createPropertyLabel({
950
1006
  class: 'nrchkb-characteristic-property-label',
951
- htmlFor: 'property-maxDataLen'
1007
+ for: maxDataLenFieldId
952
1008
  }, 'fa-database', 'Maximum Data Length').appendTo(maxDataLenRow)
953
1009
  $('<input/>', {
954
1010
  class: 'property-maxDataLen',
1011
+ id: maxDataLenFieldId,
955
1012
  type: 'number'
956
1013
  }).appendTo(maxDataLenRow).val(props.maxDataLen)
957
1014
 
958
1015
  const validValuesRow = $('<div/>', {class: 'form-row'}).appendTo(row3_properties)
1016
+ const validValuesFieldId = createFieldId('validValues')
959
1017
  createPropertyLabel({
960
1018
  class: 'nrchkb-characteristic-property-label',
961
- htmlFor: 'property-validValues'
1019
+ for: validValuesFieldId
962
1020
  }, 'fa-check-square-o', 'Valid Values').appendTo(validValuesRow)
963
1021
  $('<input/>', {
964
1022
  class: 'property-validValues',
1023
+ id: validValuesFieldId,
965
1024
  type: 'text'
966
1025
  }).appendTo(validValuesRow).val(props.validValues)
967
1026
 
968
1027
  const validValueRangesRow = $('<div/>', {class: 'form-row'}).appendTo(row3_properties)
1028
+ const validValueRangesFieldId = createFieldId('validValueRanges')
1029
+ const validValueRangesLabelId = validValueRangesFieldId + '-label'
969
1030
  const validValueRangesLabel = createPropertyLabel({
970
1031
  class: 'nrchkb-characteristic-property-label property-validValueRanges-label',
971
- htmlFor: 'property-validValueRanges'
1032
+ id: validValueRangesLabelId,
972
1033
  }, 'fa-sliders', 'Valid Value Ranges').appendTo(validValueRangesRow)
973
1034
  const validValueRangesSlider = $('<div/>', {
974
1035
  class: 'property-validValueRanges',
1036
+ id: validValueRangesFieldId,
1037
+ 'aria-labelledby': validValueRangesLabelId,
975
1038
  type: 'text'
976
1039
  }).appendTo(validValueRangesRow)
977
1040
 
@@ -984,15 +1047,19 @@
984
1047
  setIconLabelText(validValueRangesLabel, 'fa-sliders', 'Valid Value Ranges: [' + ui.values[0] + ', ' + ui.values[1] + ']')
985
1048
  }
986
1049
  })
1050
+ validValueRangesSlider.find('.ui-slider-handle')
1051
+ .attr('aria-labelledby', validValueRangesLabelId)
987
1052
  setIconLabelText(validValueRangesLabel, 'fa-sliders', 'Valid Value Ranges: [' + validValueRangesSlider.slider('values', 0) + ', ' + validValueRangesSlider.slider('values', 1) + ']')
988
1053
 
989
1054
  const adminOnlyAccessRow = $('<div/>', {class: 'form-row'}).appendTo(row3_properties)
1055
+ const adminOnlyAccessFieldId = createFieldId('adminOnlyAccess')
990
1056
  createPropertyLabel({
991
1057
  class: 'nrchkb-characteristic-property-label',
992
- htmlFor: 'property-adminOnlyAccess'
1058
+ for: adminOnlyAccessFieldId
993
1059
  }, 'fa-user-secret', 'Admin Only Access').appendTo(adminOnlyAccessRow)
994
1060
  const adminOnlyAccessSelect = $('<select/>', {
995
1061
  class: 'property-adminOnlyAccess',
1062
+ id: adminOnlyAccessFieldId,
996
1063
  multiple: 'multiple'
997
1064
  }).appendTo(adminOnlyAccessRow)
998
1065
  $('<option/>').val(0).text('READ').appendTo(adminOnlyAccessSelect)
@@ -1012,8 +1079,12 @@
1012
1079
  sortable: true
1013
1080
  })
1014
1081
 
1015
- for (let i = 0; i < nrchkbConfig.customCharacteristics.length; i++) {
1016
- const customCharacteristic = nrchkbConfig.customCharacteristics[i]
1082
+ const customCharacteristics = Array.isArray(nrchkbConfig.customCharacteristics)
1083
+ ? nrchkbConfig.customCharacteristics
1084
+ : []
1085
+
1086
+ for (let i = 0; i < customCharacteristics.length; i++) {
1087
+ const customCharacteristic = customCharacteristics[i]
1017
1088
  $('#node-input-customCharacteristics-container').editableList('addItem', customCharacteristic)
1018
1089
  }
1019
1090
  },
@@ -1030,6 +1101,25 @@
1030
1101
  }
1031
1102
  })
1032
1103
  }
1104
+
1105
+ const initNRCHKBConfigNode = function () {
1106
+ //NRCHKB Custom Characteristics
1107
+ $.ajax({
1108
+ url: 'nrchkb/config',
1109
+ dataType: 'json',
1110
+ })
1111
+ .done(function (data) {
1112
+ nrchkbConfig = data || {}
1113
+ if (!Array.isArray(nrchkbConfig.customCharacteristics)) {
1114
+ nrchkbConfig.customCharacteristics = []
1115
+ }
1116
+ })
1117
+ .fail(function () {
1118
+ nrchkbConfig = {customCharacteristics: []}
1119
+ RED.notify('Unable to load NRCHKB custom characteristics.', 'warning')
1120
+ })
1121
+ .always(registerNRCHKBConfigNode)
1122
+ }
1033
1123
  </script>
1034
1124
 
1035
1125
  <script type="text/javascript">
@@ -1064,12 +1154,14 @@
1064
1154
  $.ajax({
1065
1155
  url: 'nrchkb/info',
1066
1156
  dataType: 'json',
1067
- async: false,
1068
- success: function (data) {
1069
- nrchkbVersion = data.version
1070
- initNRCHKBConfigNode()
1071
- },
1072
1157
  })
1158
+ .done(function (data) {
1159
+ nrchkbVersion = data.version
1160
+ })
1161
+ .fail(function () {
1162
+ RED.notify('Unable to load NRCHKB version information.', 'warning')
1163
+ })
1164
+ .always(initNRCHKBConfigNode)
1073
1165
 
1074
1166
  const renderAdvertiserRecommendation = function (recommendation) {
1075
1167
  if (!recommendation || !recommendation.recommended) {
@@ -1436,12 +1528,35 @@
1436
1528
  const settingsStorageKey = 'nrchkb.pairingQR.settings'
1437
1529
  let initialized = false
1438
1530
  let refreshTimer
1531
+ let refreshRequestTimer
1439
1532
  let sidebarContent
1440
1533
  let sidebarList
1441
1534
  let sidebarStatus
1442
1535
  let settings = {
1443
1536
  autoShowWhenPairingAvailable: true,
1444
1537
  }
1538
+ const prefersReducedMotion = function () {
1539
+ return window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches
1540
+ }
1541
+ const slideDownElement = function (element, duration) {
1542
+ if (prefersReducedMotion()) {
1543
+ element.stop(true, true).show()
1544
+ return
1545
+ }
1546
+
1547
+ element.stop(true, true).slideDown(duration)
1548
+ }
1549
+ const slideUpElement = function (element, duration, onComplete) {
1550
+ if (prefersReducedMotion()) {
1551
+ element.stop(true, true).hide()
1552
+ if (onComplete) {
1553
+ onComplete()
1554
+ }
1555
+ return
1556
+ }
1557
+
1558
+ element.stop(true, true).slideUp(duration, onComplete)
1559
+ }
1445
1560
  const text = function (key, fallback) {
1446
1561
  const keys = [
1447
1562
  'node-red-contrib-homekit-bridged/' + key,
@@ -1705,7 +1820,7 @@
1705
1820
  }
1706
1821
 
1707
1822
  card.data('nrchkbRemoving', true)
1708
- card.stop(true, true).slideUp(180, function () {
1823
+ slideUpElement(card, 180, function () {
1709
1824
  card.remove()
1710
1825
  if (onComplete) {
1711
1826
  onComplete()
@@ -1779,7 +1894,7 @@
1779
1894
  .attr('data-signature', signature)
1780
1895
  .hide()
1781
1896
  sidebarList.append(newCard)
1782
- newCard.slideDown(180)
1897
+ slideDownElement(newCard, 180)
1783
1898
  })
1784
1899
  }
1785
1900
 
@@ -1789,6 +1904,13 @@
1789
1904
  })
1790
1905
  }
1791
1906
 
1907
+ const requestRefreshSidebar = function (delay, options) {
1908
+ clearTimeout(refreshRequestTimer)
1909
+ refreshRequestTimer = setTimeout(function () {
1910
+ refreshSidebar(options)
1911
+ }, delay)
1912
+ }
1913
+
1792
1914
  const registerSidebar = function () {
1793
1915
  sidebarContent = $('<div/>', {class: 'nrchkb-editor nrchkb-sidebar-pairing'})
1794
1916
  const section = $('<details/>', {class: 'nrchkb-section', open: true}).appendTo(sidebarContent)
@@ -1799,7 +1921,7 @@
1799
1921
  .appendTo(section)
1800
1922
  const sectionBody = $('<div/>', {class: 'nrchkb-section-body'}).appendTo(section)
1801
1923
  const autoShowLabel = $('<label/>', {
1802
- class: 'nrchkb-sidebar-setting nrchkb-sidebar-pairing-control',
1924
+ class: 'nrchkb-checkbox-label nrchkb-sidebar-setting nrchkb-sidebar-pairing-control',
1803
1925
  }).appendTo(sectionBody)
1804
1926
  $('<input/>', {
1805
1927
  checked: settings.autoShowWhenPairingAvailable,
@@ -1810,9 +1932,11 @@
1810
1932
  saveSettings()
1811
1933
  })
1812
1934
  .appendTo(autoShowLabel)
1813
- $('<span/>', {
1814
- text: text('qr.autoShowWhenPairingAvailable', 'Auto show if something needs pairing'),
1815
- }).appendTo(autoShowLabel)
1935
+ $('<span/>')
1936
+ .append($('<i/>', {class: 'fa fa-eye'}))
1937
+ .append(' ')
1938
+ .append(text('qr.autoShowWhenPairingAvailable', 'Auto show if something needs pairing'))
1939
+ .appendTo(autoShowLabel)
1816
1940
  sidebarStatus = $('<p/>', {
1817
1941
  class: 'nrchkb-sidebar-pairing-status',
1818
1942
  'aria-live': 'polite',
@@ -1910,14 +2034,12 @@
1910
2034
 
1911
2035
  if (RED.events) {
1912
2036
  RED.events.on('deploy', function () {
1913
- setTimeout(function () {
1914
- refreshSidebar({show: true})
1915
- }, 1500)
2037
+ requestRefreshSidebar(1500, {show: true})
1916
2038
  })
1917
2039
 
1918
2040
  ;['flows:loaded', 'nodes:add', 'nodes:change', 'nodes:remove'].forEach(function (eventName) {
1919
2041
  RED.events.on(eventName, function () {
1920
- setTimeout(refreshSidebar, 250)
2042
+ requestRefreshSidebar(250)
1921
2043
  })
1922
2044
  })
1923
2045
  }
@@ -1925,6 +2047,7 @@
1925
2047
  refreshTimer = setInterval(refreshSidebar, 10000)
1926
2048
  window.addEventListener('beforeunload', function () {
1927
2049
  clearInterval(refreshTimer)
2050
+ clearTimeout(refreshRequestTimer)
1928
2051
  })
1929
2052
  setTimeout(function () {
1930
2053
  refreshSidebar({show: true})
@@ -268,6 +268,21 @@ Generic NRCHKB plugin instance used by service nodes to track plugin dependencie
268
268
  return 'node-config-input-plugin-' + (field.path || 'field').replace(/[^a-zA-Z0-9_-]/g, '-')
269
269
  }
270
270
 
271
+ function appendFieldHelp(row, input, fieldId, field) {
272
+ const helpText = field.description || field.placeholder || ''
273
+
274
+ if (!helpText) {
275
+ return
276
+ }
277
+
278
+ const helpId = fieldId + '-help'
279
+ input.attr('aria-describedby', helpId)
280
+ $('<div/>', {
281
+ id: helpId,
282
+ class: 'nrchkb-plugin-instance-help'
283
+ }).text(helpText).appendTo(row)
284
+ }
285
+
271
286
  function renderPluginEditor(plugin, config) {
272
287
  const editorContainer = $('#node-config-input-plugin-instance-editor').empty()
273
288
 
@@ -377,6 +392,7 @@ Generic NRCHKB plugin instance used by service nodes to track plugin dependencie
377
392
  .appendTo(label)
378
393
  input.prop('checked', !!value)
379
394
  attachFieldMetadata(input, field)
395
+ appendFieldHelp(row, input, fieldId, field)
380
396
  return
381
397
  }
382
398
 
@@ -401,14 +417,6 @@ Generic NRCHKB plugin instance used by service nodes to track plugin dependencie
401
417
  rows: 4
402
418
  }).appendTo(row)
403
419
  input.val(value === undefined ? '' : value)
404
- if (field.placeholder) {
405
- const helpId = fieldId + '-help'
406
- input.attr('aria-describedby', helpId)
407
- $('<div/>', {
408
- id: helpId,
409
- class: 'nrchkb-plugin-instance-help'
410
- }).text(field.placeholder).appendTo(row)
411
- }
412
420
  } else {
413
421
  input = $('<input/>', {
414
422
  type: field.type === 'number' ? 'number' : field.type === 'password' ? 'password' : 'text',
@@ -439,6 +447,8 @@ Generic NRCHKB plugin instance used by service nodes to track plugin dependencie
439
447
  input.attr('data-plugin-dynamic-select', 'true')
440
448
  input.data('pluginDynamicValue', value === undefined ? '' : value)
441
449
  }
450
+
451
+ appendFieldHelp(row, input, fieldId, field)
442
452
  }
443
453
 
444
454
  function attachFieldMetadata(input, field) {
@@ -543,6 +543,25 @@ Send a `msg.payload` object whose keys are HomeKit characteristic names.
543
543
  oneditprepare: function () {
544
544
  let node = this
545
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
+ }
546
565
 
547
566
  if (node.isParent === false) {
548
567
  isParentToggle.val('false')
@@ -703,11 +722,11 @@ Send a `msg.payload` object whose keys are HomeKit characteristic names.
703
722
  isParentToggle
704
723
  .change(function () {
705
724
  if (isParentToggle.val() === 'true') {
706
- isLinkedDiv.fadeOut('fast')
707
- isParentDiv.fadeIn('fast')
725
+ hideEditorSection(isLinkedDiv)
726
+ showEditorSection(isParentDiv)
708
727
  } else {
709
- isParentDiv.fadeOut('fast')
710
- isLinkedDiv.fadeIn('fast')
728
+ hideEditorSection(isParentDiv)
729
+ showEditorSection(isLinkedDiv)
711
730
  selectHostType.val("_ADD_")
712
731
  }
713
732
  })
@@ -778,10 +797,10 @@ Send a `msg.payload` object whose keys are HomeKit characteristic names.
778
797
  .change(function () {
779
798
  if (this.value === 'CameraControl') {
780
799
  cameraConfiguration.prop('open', true)
781
- cameraConfiguration.fadeIn('fast')
800
+ showEditorSection(cameraConfiguration)
782
801
  node.outputs = 3
783
802
  } else {
784
- cameraConfiguration.fadeOut('fast')
803
+ hideEditorSection(cameraConfiguration)
785
804
  node.outputs = 2
786
805
  }
787
806
  })
@@ -791,9 +810,9 @@ Send a `msg.payload` object whose keys are HomeKit characteristic names.
791
810
  .change(function () {
792
811
  if (this.value === 'Lightbulb') {
793
812
  adaptiveLightningConfiguration.prop('open', true)
794
- adaptiveLightningConfiguration.fadeIn('fast')
813
+ showEditorSection(adaptiveLightningConfiguration)
795
814
  } else {
796
- adaptiveLightningConfiguration.fadeOut('fast')
815
+ hideEditorSection(adaptiveLightningConfiguration)
797
816
  }
798
817
  })
799
818
  .change()
@@ -944,6 +963,20 @@ Send a `msg.payload` object whose keys are HomeKit characteristic names.
944
963
  const selectedParentService = $('#node-input-parentService option:selected')
945
964
  node.parentService = selectedParentService.val()
946
965
  node.hostType = selectedParentService.attr("hostType")
966
+ node.bridge = ''
967
+ node.accessoryId = ''
968
+ $('#node-input-bridge').val('')
969
+ $('#node-input-accessoryId').val('')
970
+ } else if (node.hostType === '0') {
971
+ node.accessoryId = ''
972
+ node.parentService = ''
973
+ $('#node-input-accessoryId').val('')
974
+ $('#node-input-parentService').val('')
975
+ } else if (node.hostType === '1') {
976
+ node.bridge = ''
977
+ node.parentService = ''
978
+ $('#node-input-bridge').val('')
979
+ $('#node-input-parentService').val('')
947
980
  }
948
981
 
949
982
  node.serviceName = $(
@@ -5,6 +5,7 @@
5
5
  align-items: center;
6
6
  gap: 6px;
7
7
  min-width: 0;
8
+ padding-right: 112px;
8
9
  }
9
10
 
10
11
  .nrchkb-editor .nrchkb-plugins-summary::-webkit-details-marker {
@@ -12,8 +13,10 @@
12
13
  }
13
14
 
14
15
  .nrchkb-editor .nrchkb-plugin-add {
15
- margin-left: auto;
16
- justify-self: end;
16
+ position: absolute;
17
+ top: 5px;
18
+ right: 8px;
19
+ z-index: 1;
17
20
  white-space: nowrap;
18
21
  }
19
22
 
@@ -85,6 +88,16 @@
85
88
  margin-top: 8px;
86
89
  }
87
90
 
91
+ .nrchkb-editor .nrchkb-plugin-shell {
92
+ position: relative;
93
+ }
94
+
95
+ .nrchkb-editor .nrchkb-plugin-section-toolbar {
96
+ display: flex;
97
+ justify-content: flex-end;
98
+ margin: 0 0 8px;
99
+ }
100
+
88
101
  .nrchkb-editor .nrchkb-plugin-section summary {
89
102
  display: flex;
90
103
  align-items: center;
@@ -131,19 +144,24 @@
131
144
  }
132
145
 
133
146
  .nrchkb-plugin-picker-item {
147
+ display: block;
148
+ width: 100%;
134
149
  padding: 9px 10px;
135
150
  border: 1px solid var(--red-ui-secondary-border-color, #d0d0d0);
136
151
  border-radius: 6px;
137
152
  background: var(--red-ui-primary-background, #fff);
153
+ color: var(--red-ui-primary-text-color, #333);
138
154
  cursor: pointer;
155
+ font: inherit;
156
+ text-align: left;
139
157
  }
140
158
 
141
- .nrchkb-plugin-picker-item:not([aria-disabled="true"]):hover,
142
- .nrchkb-plugin-picker-item:not([aria-disabled="true"]):focus-visible {
159
+ .nrchkb-plugin-picker-item:not(:disabled):hover,
160
+ .nrchkb-plugin-picker-item:not(:disabled):focus-visible {
143
161
  border-color: var(--red-ui-focus-color, var(--red-ui-text-color-link, #1a73e8));
144
162
  }
145
163
 
146
- .nrchkb-plugin-picker-item[aria-disabled="true"] {
164
+ .nrchkb-plugin-picker-item:disabled {
147
165
  cursor: default;
148
166
  opacity: 0.55;
149
167
  }
@@ -173,18 +191,18 @@
173
191
 
174
192
  <script data-template-name="homekit-service2" type="text/x-red">
175
193
  <div class="nrchkb-editor">
176
- <details id="plugins-configuration" class="nrchkb-section">
194
+ <details id="plugins-configuration" class="nrchkb-section nrchkb-plugin-shell">
177
195
  <summary class="nrchkb-plugins-summary">
178
196
  <i class="fa fa-plug"></i>
179
197
  <span class="nrchkb-plugin-label">
180
198
  Plugins <span id="node-input-plugin-count" class="nrchkb-plugin-count">0</span>
181
199
  <span id="node-input-plugin-summary-list" class="nrchkb-plugin-summary-list">No plugins attached</span>
182
200
  </span>
183
- <button type="button" id="node-input-add-plugin" class="red-ui-button red-ui-button-small nrchkb-plugin-add">
184
- <i class="fa fa-plus"></i>
185
- Add plugin
186
- </button>
187
201
  </summary>
202
+ <button type="button" id="node-input-add-plugin" class="red-ui-button red-ui-button-small nrchkb-plugin-add">
203
+ <i class="fa fa-plus"></i>
204
+ Add plugin
205
+ </button>
188
206
  <div class="nrchkb-section-body">
189
207
  <div class="nrchkb-plugin-slot-fields" style="display: none;">
190
208
  <input type="hidden" id="node-input-plugins">
@@ -670,6 +688,35 @@ To discover valid characteristic names for the selected service, send a test pay
670
688
  let node = this
671
689
  let isParentToggle = $('#node-config-input-isParent')
672
690
  const cameraPluginId = 'node-red-contrib-homekit-bridged:homebridge-camera-ffmpeg'
691
+ const prefersReducedMotion = function () {
692
+ return window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches
693
+ }
694
+ const showEditorSection = function (section) {
695
+ if (prefersReducedMotion()) {
696
+ section.stop(true, true).show()
697
+ return
698
+ }
699
+
700
+ section.stop(true, true).fadeIn('fast')
701
+ }
702
+ const hideEditorSection = function (section) {
703
+ if (prefersReducedMotion()) {
704
+ section.stop(true, true).hide()
705
+ return
706
+ }
707
+
708
+ section.stop(true, true).fadeOut('fast')
709
+ }
710
+ const scrollEditorSectionIntoView = function (element) {
711
+ if (!element || !element.scrollIntoView) {
712
+ return
713
+ }
714
+
715
+ element.scrollIntoView({
716
+ behavior: prefersReducedMotion() ? 'auto' : 'smooth',
717
+ block: 'start'
718
+ })
719
+ }
673
720
 
674
721
  if (node.isParent === false) {
675
722
  isParentToggle.val('false')
@@ -685,11 +732,11 @@ To discover valid characteristic names for the selected service, send a test pay
685
732
  isParentToggle
686
733
  .change(function () {
687
734
  if (isParentToggle.val() === 'true') {
688
- isLinkedDiv.fadeOut('fast')
689
- isParentDiv.fadeIn('fast')
735
+ hideEditorSection(isLinkedDiv)
736
+ showEditorSection(isParentDiv)
690
737
  } else {
691
- isParentDiv.fadeOut('fast')
692
- isLinkedDiv.fadeIn('fast')
738
+ hideEditorSection(isParentDiv)
739
+ showEditorSection(isLinkedDiv)
693
740
  selectHostType.val("_ADD_")
694
741
  }
695
742
  })
@@ -1069,6 +1116,21 @@ To discover valid characteristic names for the selected service, send a test pay
1069
1116
  return sectionId + '-' + (field.path || 'field').replace(/[^a-zA-Z0-9_-]/g, '-')
1070
1117
  }
1071
1118
 
1119
+ const appendPluginFieldHelp = function (row, input, fieldId, field) {
1120
+ const helpText = field.description || field.placeholder || ''
1121
+
1122
+ if (!helpText) {
1123
+ return
1124
+ }
1125
+
1126
+ const helpId = fieldId + '-help'
1127
+ input.attr('aria-describedby', helpId)
1128
+ $('<div/>', {
1129
+ id: helpId,
1130
+ class: 'nrchkb-plugin-instance-help'
1131
+ }).text(helpText).appendTo(row)
1132
+ }
1133
+
1072
1134
  const renderPluginField = function (body, section, field, config) {
1073
1135
  const sectionId = section.attr('id')
1074
1136
  const fieldId = createPluginFieldId(sectionId, field)
@@ -1130,14 +1192,6 @@ To discover valid characteristic names for the selected service, send a test pay
1130
1192
  rows: 4
1131
1193
  }).appendTo(row)
1132
1194
  input.val(value === undefined ? '' : value)
1133
- if (field.placeholder) {
1134
- const helpId = fieldId + '-help'
1135
- input.attr('aria-describedby', helpId)
1136
- $('<div/>', {
1137
- id: helpId,
1138
- class: 'nrchkb-plugin-instance-help'
1139
- }).text(field.placeholder).appendTo(row)
1140
- }
1141
1195
  } else {
1142
1196
  input = $('<input/>', {
1143
1197
  type: field.type === 'number' ? 'number' : field.type === 'password' ? 'password' : 'text',
@@ -1173,6 +1227,8 @@ To discover valid characteristic names for the selected service, send a test pay
1173
1227
  input.attr('data-plugin-dynamic-select', 'true')
1174
1228
  input.data('pluginDynamicValue', value === undefined ? '' : value)
1175
1229
  }
1230
+
1231
+ appendPluginFieldHelp(row, input, fieldId, field)
1176
1232
  }
1177
1233
 
1178
1234
  const loadDynamicPluginSelect = function (section, input) {
@@ -1315,6 +1371,9 @@ To discover valid characteristic names for the selected service, send a test pay
1315
1371
  const summary = $('<summary/>').appendTo(section)
1316
1372
  $('<i/>', {class: 'fa fa-plug'}).appendTo(summary)
1317
1373
  $('<span/>', {class: 'nrchkb-plugin-title'}).text(title).appendTo(summary)
1374
+
1375
+ const body = $('<div/>', {class: 'nrchkb-section-body'}).appendTo(section)
1376
+ const toolbar = $('<div/>', {class: 'nrchkb-plugin-section-toolbar'}).appendTo(body)
1318
1377
  $('<button/>', {
1319
1378
  type: 'button',
1320
1379
  class: 'red-ui-button red-ui-button-small nrchkb-plugin-remove',
@@ -1322,9 +1381,8 @@ To discover valid characteristic names for the selected service, send a test pay
1322
1381
  title: 'Remove plugin'
1323
1382
  })
1324
1383
  .append($('<i/>', {class: 'fa fa-remove'}))
1325
- .appendTo(summary)
1384
+ .appendTo(toolbar)
1326
1385
 
1327
- const body = $('<div/>', {class: 'nrchkb-section-body'}).appendTo(section)
1328
1386
  renderPluginEditor(body, entry, plugin || {}, section)
1329
1387
  })
1330
1388
 
@@ -1332,6 +1390,11 @@ To discover valid characteristic names for the selected service, send a test pay
1332
1390
  renderConfigNodePluginSection(entry, getPluginMetadata(entry.id), index)
1333
1391
  })
1334
1392
  }
1393
+ let pluginRenderTimer
1394
+ const schedulePluginSectionsRefresh = function () {
1395
+ clearTimeout(pluginRenderTimer)
1396
+ pluginRenderTimer = window.setTimeout(renderPluginSections, 250)
1397
+ }
1335
1398
 
1336
1399
  const pluginSlotNames = [
1337
1400
  'plugin1',
@@ -1448,6 +1511,9 @@ To discover valid characteristic names for the selected service, send a test pay
1448
1511
  const summary = $('<summary/>').appendTo(section)
1449
1512
  $('<i/>', {class: 'fa fa-plug'}).appendTo(summary)
1450
1513
  $('<span/>', {class: 'nrchkb-plugin-title'}).text(title).appendTo(summary)
1514
+
1515
+ const body = $('<div/>', {class: 'nrchkb-section-body'}).appendTo(section)
1516
+ const toolbar = $('<div/>', {class: 'nrchkb-plugin-section-toolbar'}).appendTo(body)
1451
1517
  $('<button/>', {
1452
1518
  type: 'button',
1453
1519
  class: 'red-ui-button red-ui-button-small nrchkb-plugin-remove',
@@ -1456,9 +1522,8 @@ To discover valid characteristic names for the selected service, send a test pay
1456
1522
  'data-slot': entry.slot
1457
1523
  })
1458
1524
  .append($('<i/>', {class: 'fa fa-remove'}))
1459
- .appendTo(summary)
1525
+ .appendTo(toolbar)
1460
1526
 
1461
- const body = $('<div/>', {class: 'nrchkb-section-body'}).appendTo(section)
1462
1527
  if (plugin) {
1463
1528
  body.append(createPluginAbout(plugin))
1464
1529
  }
@@ -1489,8 +1554,7 @@ To discover valid characteristic names for the selected service, send a test pay
1489
1554
  }
1490
1555
  RED.editor.editConfig(slot, 'homekit-plugin-instance', '_ADD_', 'node-input', node)
1491
1556
  pluginSetupComplete = true
1492
- window.setTimeout(renderPluginSections, 100)
1493
- window.setTimeout(renderPluginSections, 500)
1557
+ schedulePluginSectionsRefresh()
1494
1558
  }
1495
1559
 
1496
1560
  const addPluginEntry = function (entry) {
@@ -1540,12 +1604,11 @@ To discover valid characteristic names for the selected service, send a test pay
1540
1604
  : !duplicateAllowed
1541
1605
  ? 'Already added'
1542
1606
  : renderCompatibilityText(plugin)
1543
- const item = $('<div/>', {
1607
+ const item = $('<button/>', {
1544
1608
  class: 'nrchkb-plugin-picker-item',
1545
- 'aria-disabled': disabled ? 'true' : 'false',
1546
1609
  'aria-label': getPluginDisplayLabel(plugin) + '. ' + reason,
1547
- role: 'button',
1548
- tabindex: disabled ? '-1' : '0'
1610
+ disabled,
1611
+ type: 'button'
1549
1612
  }).appendTo(dialog)
1550
1613
 
1551
1614
  $('<div/>', {class: 'nrchkb-plugin-picker-title'})
@@ -1573,20 +1636,6 @@ To discover valid characteristic names for the selected service, send a test pay
1573
1636
  }
1574
1637
 
1575
1638
  item.on('click', addSelectedPlugin)
1576
- item.on('keydown', function (event) {
1577
- if (event.key === 'Enter') {
1578
- event.preventDefault()
1579
- addSelectedPlugin()
1580
- } else if (event.key === ' ') {
1581
- event.preventDefault()
1582
- }
1583
- })
1584
- item.on('keyup', function (event) {
1585
- if (event.key === ' ') {
1586
- event.preventDefault()
1587
- addSelectedPlugin()
1588
- }
1589
- })
1590
1639
  }
1591
1640
  })
1592
1641
 
@@ -1672,8 +1721,7 @@ To discover valid characteristic names for the selected service, send a test pay
1672
1721
  const configNodeId = $(this).attr('data-config-id') || getPluginSlotValue(slot)
1673
1722
  if (RED.editor && RED.editor.editConfig) {
1674
1723
  RED.editor.editConfig(slot, 'homekit-plugin-instance', configNodeId || '_ADD_', 'node-input', node)
1675
- window.setTimeout(renderPluginSections, 100)
1676
- window.setTimeout(renderPluginSections, 500)
1724
+ schedulePluginSectionsRefresh()
1677
1725
  }
1678
1726
  return
1679
1727
  }
@@ -1687,12 +1735,7 @@ To discover valid characteristic names for the selected service, send a test pay
1687
1735
 
1688
1736
  section.prop('open', true)
1689
1737
 
1690
- if (section[0] && section[0].scrollIntoView) {
1691
- section[0].scrollIntoView({
1692
- behavior: 'smooth',
1693
- block: 'start'
1694
- })
1695
- }
1738
+ scrollEditorSectionIntoView(section[0])
1696
1739
  })
1697
1740
 
1698
1741
  pluginOverview.on('click', '.nrchkb-plugin-overview-remove', function (event) {
@@ -1832,9 +1875,9 @@ To discover valid characteristic names for the selected service, send a test pay
1832
1875
  .change(function () {
1833
1876
  if (this.value === 'Lightbulb') {
1834
1877
  adaptiveLightningConfiguration.prop('open', true)
1835
- adaptiveLightningConfiguration.fadeIn('fast')
1878
+ showEditorSection(adaptiveLightningConfiguration)
1836
1879
  } else {
1837
- adaptiveLightningConfiguration.fadeOut('fast')
1880
+ hideEditorSection(adaptiveLightningConfiguration)
1838
1881
  }
1839
1882
  })
1840
1883
  .change()
@@ -1967,6 +2010,20 @@ To discover valid characteristic names for the selected service, send a test pay
1967
2010
  const selectedParentService = $('#node-input-parentService option:selected')
1968
2011
  node.parentService = selectedParentService.val()
1969
2012
  node.hostType = selectedParentService.attr("hostType")
2013
+ node.bridge = ''
2014
+ node.accessoryId = ''
2015
+ $('#node-input-bridge').val('')
2016
+ $('#node-input-accessoryId').val('')
2017
+ } else if (node.hostType === '0') {
2018
+ node.accessoryId = ''
2019
+ node.parentService = ''
2020
+ $('#node-input-accessoryId').val('')
2021
+ $('#node-input-parentService').val('')
2022
+ } else if (node.hostType === '1') {
2023
+ node.bridge = ''
2024
+ node.parentService = ''
2025
+ $('#node-input-bridge').val('')
2026
+ $('#node-input-parentService').val('')
1970
2027
  }
1971
2028
 
1972
2029
  node.serviceName = $(
@@ -18,7 +18,7 @@
18
18
  </div>
19
19
  <div class="form-row">
20
20
  <label for="node-config-input-pinCode"><i class="fa fa-key"></i> Pin Code</label>
21
- <input type="text" id="node-config-input-pinCode" placeholder="xxxx-xxxx">
21
+ <input type="text" id="node-config-input-pinCode" placeholder="xxxx-xxxx" inputmode="numeric" autocomplete="off" spellcheck="false">
22
22
  </div>
23
23
  <div class="form-row nrchkb-checkbox-row">
24
24
  <label class="nrchkb-checkbox-label" for="node-config-input-allowInsecureRequest">
@@ -40,7 +40,7 @@
40
40
  <div class="nrchkb-section-body">
41
41
  <div class="form-row">
42
42
  <label for="node-config-input-port"><i class="fa fa-plug"></i> Port</label>
43
- <input type="text" id="node-config-input-port" placeholder="Leave blank to auto assign">
43
+ <input type="text" id="node-config-input-port" placeholder="Leave blank to auto assign" inputmode="numeric" autocomplete="off" spellcheck="false">
44
44
  </div>
45
45
  <div class="form-row">
46
46
  <label for="node-config-input-advertiser"><i class="fa fa-bullhorn"></i> Advertiser</label>
@@ -53,7 +53,7 @@
53
53
  </div>
54
54
  <div class="form-row">
55
55
  <label for="node-config-input-bind"><i class="fa fa-sitemap"></i> Bind</label>
56
- <input type="text" id="node-config-input-bind" placeholder="::">
56
+ <input type="text" id="node-config-input-bind" placeholder="::" autocomplete="off" spellcheck="false">
57
57
  <input type="hidden" id="node-config-input-bindType">
58
58
  </div>
59
59
  </div>
@@ -304,6 +304,25 @@ Defines a HomeKit accessory that is paired directly in the Apple Home app. Use i
304
304
  },
305
305
  oneditprepare: function () {
306
306
  const node = this
307
+ const prefersReducedMotion = function () {
308
+ return window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches
309
+ }
310
+ const showEditorSection = function (section) {
311
+ if (prefersReducedMotion()) {
312
+ section.stop(true, true).show()
313
+ return
314
+ }
315
+
316
+ section.stop(true, true).fadeIn('fast')
317
+ }
318
+ const hideEditorSection = function (section) {
319
+ if (prefersReducedMotion()) {
320
+ section.stop(true, true).hide()
321
+ return
322
+ }
323
+
324
+ section.stop(true, true).fadeOut('fast')
325
+ }
307
326
 
308
327
  if (!validatePinCode(node.pinCode)) {
309
328
  node.pinCode = generatePinCode()
@@ -325,9 +344,9 @@ Defines a HomeKit accessory that is paired directly in the Apple Home app. Use i
325
344
  .change(function () {
326
345
  if (this.checked) {
327
346
  mdnsConfigurationSection.prop('open', true)
328
- mdnsConfiguration.fadeIn('fast')
347
+ showEditorSection(mdnsConfiguration)
329
348
  } else {
330
- mdnsConfiguration.fadeOut('fast')
349
+ hideEditorSection(mdnsConfiguration)
331
350
  }
332
351
  })
333
352
  .change()
@@ -15,19 +15,19 @@
15
15
  </div>
16
16
  <div class="form-row">
17
17
  <label for="node-config-input-address"><i class="fa fa-globe"></i> Address</label>
18
- <input type="text" id="node-config-input-address" placeholder="192.168.1.1">
18
+ <input type="text" id="node-config-input-address" placeholder="192.168.1.1" autocomplete="url" spellcheck="false">
19
19
  </div>
20
20
  <div class="form-row">
21
21
  <label for="node-config-input-username"><i class="fa fa-user"></i> Username</label>
22
- <input type="text" id="node-config-input-username">
22
+ <input type="text" id="node-config-input-username" autocomplete="username" spellcheck="false">
23
23
  </div>
24
24
  <div class="form-row">
25
25
  <label for="node-config-input-password"><i class="fa fa-lock"></i> Password</label>
26
- <input type="password" id="node-config-input-password">
26
+ <input type="password" id="node-config-input-password" autocomplete="current-password">
27
27
  </div>
28
28
  <div class="form-row">
29
29
  <label for="node-config-input-overrideAddress"><i class="fa fa-random"></i> Override Address</label>
30
- <input type="text" id="node-config-input-overrideAddress" placeholder="Optional livestream address">
30
+ <input type="text" id="node-config-input-overrideAddress" placeholder="Optional livestream address" autocomplete="url" spellcheck="false">
31
31
  </div>
32
32
  <div class="form-row nrchkb-checkbox-row">
33
33
  <label class="nrchkb-checkbox-label" for="node-config-input-allowSelfSigned">
File without changes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "node-red-contrib-homekit-bridged",
3
- "version": "2.0.0-dev.7",
3
+ "version": "2.0.0-dev.9",
4
4
  "description": "Node-RED nodes to simulate Apple HomeKit devices.",
5
5
  "main": "build/nodes/nrchkb.js",
6
6
  "scripts": {