homebridge-roborock-matter 3.12.1 → 3.12.2

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.
@@ -561,9 +561,9 @@ async function handleSaveClick(button) {
561
561
  }
562
562
  }
563
563
 
564
- /** An auto-save that cannot fail silently. */
564
+ /** An auto-save that cannot fail silently, and cannot write the panel below. */
565
565
  function autoSave() {
566
- saveCredentials(false).catch(() =>
566
+ saveCredentials(false, { only: AUTO_SAVED_FIELDS }).catch(() =>
567
567
  showToast("error", "Could not save that change.")
568
568
  );
569
569
  }
@@ -586,6 +586,50 @@ function getCode() {
586
586
  return elements.code.value.trim();
587
587
  }
588
588
 
589
+ /**
590
+ * The keys an implicit save is allowed to write.
591
+ *
592
+ * Every control wired to `autoSave()` lives in the Account panel or the
593
+ * advanced block inside it, and the device rows only ever edit `skipDevices`.
594
+ * The Apple Home checkboxes sit in their own panel with their own Save button
595
+ * and deliberately have no autoSave binding — but the implicit saves spread the
596
+ * WHOLE form, so a change to debug mode, region, email or a device row
597
+ * committed whatever those four Apple Home keys happened to be in the DOM at
598
+ * that moment. Two ways that goes wrong, and the first one cost nine
599
+ * accessories three times in one day:
600
+ *
601
+ * - a box was unticked and NOT saved, and an unrelated change persisted it.
602
+ * On 20 Aug `debugMode` went `false` -> `true` and
603
+ * `enableHomeKitStateSensors` went `true` -> `false` in the SAME write: one
604
+ * debug-mode toggle, and the untouched checkbox rode along.
605
+ * - the same in reverse — a stray tick committed without a Save.
606
+ *
607
+ * `updatePluginConfig` is a merge, so leaving a key out of the patch keeps
608
+ * whatever is saved. That is the whole fix: an implicit save writes the fields
609
+ * whose own controls triggered it, and nothing else. The password is off the
610
+ * list for the same reason `login()` deletes it — an email-field blur should
611
+ * not write an account password into config.json in cleartext.
612
+ */
613
+ const AUTO_SAVED_FIELDS = [
614
+ "email",
615
+ "baseURL",
616
+ "skipDevices",
617
+ "debugMode",
618
+ "matterChargedBatteryThreshold",
619
+ "preferCloudForMatterCommands",
620
+ "cloudOnlyMode",
621
+ "transientWarningThrottleHours",
622
+ ];
623
+
624
+ /** The listed keys only. Absent keys survive, because the save is a merge. */
625
+ function pickFields(source, keys) {
626
+ const picked = {};
627
+ for (const key of keys) {
628
+ picked[key] = source[key];
629
+ }
630
+ return picked;
631
+ }
632
+
589
633
  function getFormValues() {
590
634
  return {
591
635
  email: getEmail(),
@@ -733,7 +777,9 @@ function onManagedDeviceToggle(device, managed) {
733
777
 
734
778
  setSkipTokens(tokens);
735
779
  rerenderMatterPairing();
736
- saveCredentials(false)
780
+ // A device row edits `skipDevices`. It has no business saving the Apple Home
781
+ // panel on the way past.
782
+ saveCredentials(false, { only: AUTO_SAVED_FIELDS })
737
783
  .then(() => {
738
784
  showToast(
739
785
  "success",
@@ -748,7 +794,13 @@ function onManagedDeviceToggle(device, managed) {
748
794
  renderManagedDevices();
749
795
  }
750
796
 
751
- async function saveCredentials(showSuccess = false) {
797
+ /**
798
+ * @param {boolean} showSuccess
799
+ * @param {{only?: string[] | null}} [options] `only` narrows the patch to those
800
+ * keys — see AUTO_SAVED_FIELDS. Omit it for the explicit Save buttons, which
801
+ * are the only thing allowed to write the whole form.
802
+ */
803
+ async function saveCredentials(showSuccess = false, { only = null } = {}) {
752
804
  const formValues = getFormValues();
753
805
  const { email, password } = formValues;
754
806
  if (!email) {
@@ -756,10 +808,12 @@ async function saveCredentials(showSuccess = false) {
756
808
  return;
757
809
  }
758
810
 
759
- const patch = {
760
- ...formValues,
761
- enableMatterServiceAreaBeta: undefined,
762
- };
811
+ const patch = only
812
+ ? pickFields(formValues, only)
813
+ : {
814
+ ...formValues,
815
+ enableMatterServiceAreaBeta: undefined,
816
+ };
763
817
 
764
818
  if (!password) {
765
819
  delete patch.password;
@@ -767,7 +821,8 @@ async function saveCredentials(showSuccess = false) {
767
821
 
768
822
  await updatePluginConfig(patch);
769
823
 
770
- if (password) {
824
+ // Only when this save actually carried it. A narrowed patch never does.
825
+ if (password && !only) {
771
826
  state.hasPassword = true;
772
827
  }
773
828
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "homebridge-roborock-matter",
3
- "version": "3.12.1",
3
+ "version": "3.12.2",
4
4
  "description": "The most complete Roborock plugin for Apple Home. Supports the entire Roborock lineup — from the classic S-series to the new 2025 Q7 series that no other plugin can control. Sign in with your Roborock account and get native start/stop, room cleaning, suction levels, battery, and live 'cleaning in the kitchen' room tracking. Verified by Homebridge.",
5
5
  "license": "MIT",
6
6
  "author": {