ソースを参照

Allow prompting for erase (#134)

Paulus Schoutsen 3 年 前
コミット
c4daee18bc
7 ファイル変更159 行追加75 行削除
  1. 8 8
      index.html
  2. 51 53
      package-lock.json
  3. 1 1
      package.json
  4. 14 0
      src/components/ewt-checkbox.ts
  5. 14 0
      src/components/ewt-formfield.ts
  6. 2 0
      src/const.ts
  7. 69 13
      src/install-dialog.ts

+ 8 - 8
index.html

@@ -329,7 +329,7 @@
   "name": "ESPHome",
   "version": "2021.11.0",
   "home_assistant_domain": "esphome",
-  "new_install_skip_erase": false,
+  "new_install_prompt_erase": false,
   "builds": [
     {
       "chipFamily": "ESP32",
@@ -350,7 +350,7 @@
 }</pre
       >
       <p>
-        Each build contains a list of parts to be flashed to the ESP device.
+        Each build contains a list of parts to be installed to the ESP device.
         Each part consists of a path to the file and an offset on the flash
         where it should be installed. Part paths are resolved relative to the
         path of the manifest, but can also be URLs to other hosts.
@@ -361,12 +361,12 @@
         Tools will link the user to add this device to Home Assistant.
       </p>
       <p>
-        By default a new installation will erase the entire flash. If you want
-        to skip this step, set the optional key
-        <code>new_install_skip_erase</code> to <code>true</code>. ESP Web Tools
-        offers users a new installation if it is unable to detect the current
-        firmware of the device (via Improv Serial) or if the detected firmware
-        does not match the name specififed in the manifest.
+        By default a new installation will erase all data before installation.
+        If you want to leave this choice to the user, set the optional key
+        <code>new_install_prompt_erase</code> to <code>true</code>. ESP Web
+        Tools offers users a new installation if it is unable to detect the
+        current firmware of the device (via Improv Serial) or if the detected
+        firmware does not match the name specififed in the manifest.
       </p>
       <h2 id="improv">Configuring Wi-Fi</h2>
       <p>

+ 51 - 53
package-lock.json

@@ -13,8 +13,8 @@
         "@material/mwc-checkbox": "^0.25.3",
         "@material/mwc-circular-progress": "^0.25.3",
         "@material/mwc-dialog": "^0.25.3",
+        "@material/mwc-formfield": "^0.25.3",
         "@material/mwc-icon-button": "^0.25.3",
-        "@material/mwc-linear-progress": "^0.25.1",
         "@material/mwc-textfield": "^0.25.3",
         "esp-web-flasher": "^4.0.0",
         "improv-wifi-serial-sdk": "^2.1.0",
@@ -188,6 +188,20 @@
         "tslib": "^2.1.0"
       }
     },
+    "node_modules/@material/form-field": {
+      "version": "14.0.0-canary.261f2db59.0",
+      "resolved": "https://registry.npmjs.org/@material/form-field/-/form-field-14.0.0-canary.261f2db59.0.tgz",
+      "integrity": "sha512-NCc/o60gwuF28PVMgFkHrKcHxIaCMZK9JRVfoaD0sF2BINYrjaCkFZ+x6AhNjAWLUQMhJMfc+1WXAUE2T85Mug==",
+      "dependencies": {
+        "@material/base": "14.0.0-canary.261f2db59.0",
+        "@material/feature-targeting": "14.0.0-canary.261f2db59.0",
+        "@material/ripple": "14.0.0-canary.261f2db59.0",
+        "@material/rtl": "14.0.0-canary.261f2db59.0",
+        "@material/theme": "14.0.0-canary.261f2db59.0",
+        "@material/typography": "14.0.0-canary.261f2db59.0",
+        "tslib": "^2.1.0"
+      }
+    },
     "node_modules/@material/icon-button": {
       "version": "14.0.0-canary.261f2db59.0",
       "resolved": "https://registry.npmjs.org/@material/icon-button/-/icon-button-14.0.0-canary.261f2db59.0.tgz",
@@ -216,20 +230,6 @@
         "tslib": "^2.1.0"
       }
     },
-    "node_modules/@material/linear-progress": {
-      "version": "14.0.0-canary.261f2db59.0",
-      "resolved": "https://registry.npmjs.org/@material/linear-progress/-/linear-progress-14.0.0-canary.261f2db59.0.tgz",
-      "integrity": "sha512-qP/iI4CT7i7HhXuUiNWL5pDN6tyTJ4uLl8e9QImz4mcQLUMU3xrNBIsutS+I5GnBE8FwLDozZFccfCxHh+pvzw==",
-      "dependencies": {
-        "@material/animation": "14.0.0-canary.261f2db59.0",
-        "@material/base": "14.0.0-canary.261f2db59.0",
-        "@material/feature-targeting": "14.0.0-canary.261f2db59.0",
-        "@material/progress-indicator": "14.0.0-canary.261f2db59.0",
-        "@material/rtl": "14.0.0-canary.261f2db59.0",
-        "@material/theme": "14.0.0-canary.261f2db59.0",
-        "tslib": "^2.1.0"
-      }
-    },
     "node_modules/@material/mwc-base": {
       "version": "0.25.3",
       "resolved": "https://registry.npmjs.org/@material/mwc-base/-/mwc-base-0.25.3.tgz",
@@ -301,6 +301,17 @@
         "tslib": "^2.0.1"
       }
     },
+    "node_modules/@material/mwc-formfield": {
+      "version": "0.25.3",
+      "resolved": "https://registry.npmjs.org/@material/mwc-formfield/-/mwc-formfield-0.25.3.tgz",
+      "integrity": "sha512-JP/ZgsWok0ZVwUQfYgaov0Ocn1zDiiw7Po6q8k/n5tOS67S41XUB/ctiUg1gh00LAM0v3eZAexa9ZmKarviVJA==",
+      "dependencies": {
+        "@material/form-field": "=14.0.0-canary.261f2db59.0",
+        "@material/mwc-base": "^0.25.3",
+        "lit": "^2.0.0",
+        "tslib": "^2.0.1"
+      }
+    },
     "node_modules/@material/mwc-icon": {
       "version": "0.25.3",
       "resolved": "https://registry.npmjs.org/@material/mwc-icon/-/mwc-icon-0.25.3.tgz",
@@ -330,18 +341,6 @@
         "tslib": "^2.0.1"
       }
     },
-    "node_modules/@material/mwc-linear-progress": {
-      "version": "0.25.3",
-      "resolved": "https://registry.npmjs.org/@material/mwc-linear-progress/-/mwc-linear-progress-0.25.3.tgz",
-      "integrity": "sha512-iAsM5zo78rYXXzwEg1RT0+jShxkUjOEUK0Yj6KxqxbXy1VKPTZ4HRB5Fy6wChWQi/Xl8wFlfI6nNAQtH7pakeA==",
-      "dependencies": {
-        "@material/linear-progress": "=14.0.0-canary.261f2db59.0",
-        "@material/mwc-base": "^0.25.3",
-        "@material/theme": "=14.0.0-canary.261f2db59.0",
-        "lit": "^2.0.0",
-        "tslib": "^2.0.1"
-      }
-    },
     "node_modules/@material/mwc-notched-outline": {
       "version": "0.25.3",
       "resolved": "https://registry.npmjs.org/@material/mwc-notched-outline/-/mwc-notched-outline-0.25.3.tgz",
@@ -2060,6 +2059,20 @@
         "tslib": "^2.1.0"
       }
     },
+    "@material/form-field": {
+      "version": "14.0.0-canary.261f2db59.0",
+      "resolved": "https://registry.npmjs.org/@material/form-field/-/form-field-14.0.0-canary.261f2db59.0.tgz",
+      "integrity": "sha512-NCc/o60gwuF28PVMgFkHrKcHxIaCMZK9JRVfoaD0sF2BINYrjaCkFZ+x6AhNjAWLUQMhJMfc+1WXAUE2T85Mug==",
+      "requires": {
+        "@material/base": "14.0.0-canary.261f2db59.0",
+        "@material/feature-targeting": "14.0.0-canary.261f2db59.0",
+        "@material/ripple": "14.0.0-canary.261f2db59.0",
+        "@material/rtl": "14.0.0-canary.261f2db59.0",
+        "@material/theme": "14.0.0-canary.261f2db59.0",
+        "@material/typography": "14.0.0-canary.261f2db59.0",
+        "tslib": "^2.1.0"
+      }
+    },
     "@material/icon-button": {
       "version": "14.0.0-canary.261f2db59.0",
       "resolved": "https://registry.npmjs.org/@material/icon-button/-/icon-button-14.0.0-canary.261f2db59.0.tgz",
@@ -2088,20 +2101,6 @@
         "tslib": "^2.1.0"
       }
     },
-    "@material/linear-progress": {
-      "version": "14.0.0-canary.261f2db59.0",
-      "resolved": "https://registry.npmjs.org/@material/linear-progress/-/linear-progress-14.0.0-canary.261f2db59.0.tgz",
-      "integrity": "sha512-qP/iI4CT7i7HhXuUiNWL5pDN6tyTJ4uLl8e9QImz4mcQLUMU3xrNBIsutS+I5GnBE8FwLDozZFccfCxHh+pvzw==",
-      "requires": {
-        "@material/animation": "14.0.0-canary.261f2db59.0",
-        "@material/base": "14.0.0-canary.261f2db59.0",
-        "@material/feature-targeting": "14.0.0-canary.261f2db59.0",
-        "@material/progress-indicator": "14.0.0-canary.261f2db59.0",
-        "@material/rtl": "14.0.0-canary.261f2db59.0",
-        "@material/theme": "14.0.0-canary.261f2db59.0",
-        "tslib": "^2.1.0"
-      }
-    },
     "@material/mwc-base": {
       "version": "0.25.3",
       "resolved": "https://registry.npmjs.org/@material/mwc-base/-/mwc-base-0.25.3.tgz",
@@ -2173,6 +2172,17 @@
         "tslib": "^2.0.1"
       }
     },
+    "@material/mwc-formfield": {
+      "version": "0.25.3",
+      "resolved": "https://registry.npmjs.org/@material/mwc-formfield/-/mwc-formfield-0.25.3.tgz",
+      "integrity": "sha512-JP/ZgsWok0ZVwUQfYgaov0Ocn1zDiiw7Po6q8k/n5tOS67S41XUB/ctiUg1gh00LAM0v3eZAexa9ZmKarviVJA==",
+      "requires": {
+        "@material/form-field": "=14.0.0-canary.261f2db59.0",
+        "@material/mwc-base": "^0.25.3",
+        "lit": "^2.0.0",
+        "tslib": "^2.0.1"
+      }
+    },
     "@material/mwc-icon": {
       "version": "0.25.3",
       "resolved": "https://registry.npmjs.org/@material/mwc-icon/-/mwc-icon-0.25.3.tgz",
@@ -2202,18 +2212,6 @@
         "tslib": "^2.0.1"
       }
     },
-    "@material/mwc-linear-progress": {
-      "version": "0.25.3",
-      "resolved": "https://registry.npmjs.org/@material/mwc-linear-progress/-/mwc-linear-progress-0.25.3.tgz",
-      "integrity": "sha512-iAsM5zo78rYXXzwEg1RT0+jShxkUjOEUK0Yj6KxqxbXy1VKPTZ4HRB5Fy6wChWQi/Xl8wFlfI6nNAQtH7pakeA==",
-      "requires": {
-        "@material/linear-progress": "=14.0.0-canary.261f2db59.0",
-        "@material/mwc-base": "^0.25.3",
-        "@material/theme": "=14.0.0-canary.261f2db59.0",
-        "lit": "^2.0.0",
-        "tslib": "^2.0.1"
-      }
-    },
     "@material/mwc-notched-outline": {
       "version": "0.25.3",
       "resolved": "https://registry.npmjs.org/@material/mwc-notched-outline/-/mwc-notched-outline-0.25.3.tgz",

+ 1 - 1
package.json

@@ -25,8 +25,8 @@
     "@material/mwc-checkbox": "^0.25.3",
     "@material/mwc-circular-progress": "^0.25.3",
     "@material/mwc-dialog": "^0.25.3",
+    "@material/mwc-formfield": "^0.25.3",
     "@material/mwc-icon-button": "^0.25.3",
-    "@material/mwc-linear-progress": "^0.25.1",
     "@material/mwc-textfield": "^0.25.3",
     "esp-web-flasher": "^4.0.0",
     "improv-wifi-serial-sdk": "^2.1.0",

+ 14 - 0
src/components/ewt-checkbox.ts

@@ -0,0 +1,14 @@
+import { CheckboxBase } from "@material/mwc-checkbox/mwc-checkbox-base";
+import { styles } from "@material/mwc-checkbox/mwc-checkbox.css";
+
+declare global {
+  interface HTMLElementTagNameMap {
+    "ewt-checkbox": EwtCheckbox;
+  }
+}
+
+export class EwtCheckbox extends CheckboxBase {
+  static override styles = [styles];
+}
+
+customElements.define("ewt-checkbox", EwtCheckbox);

+ 14 - 0
src/components/ewt-formfield.ts

@@ -0,0 +1,14 @@
+import { FormfieldBase } from "@material/mwc-formfield/mwc-formfield-base";
+import { styles } from "@material/mwc-formfield/mwc-formfield.css";
+
+declare global {
+  interface HTMLElementTagNameMap {
+    "ewt-formfield": EwtFormfield;
+  }
+}
+
+export class EwtFormfield extends FormfieldBase {
+  static override styles = [styles];
+}
+
+customElements.define("ewt-formfield", EwtFormfield);

+ 2 - 0
src/const.ts

@@ -16,7 +16,9 @@ export interface Manifest {
   name: string;
   version: string;
   home_assistant_domain?: string;
+  /** @deprecated use `new_install_prompt_erase` instead */
   new_install_skip_erase?: boolean;
+  new_install_prompt_erase?: boolean;
   builds: Build[];
 }
 

+ 69 - 13
src/install-dialog.ts

@@ -4,6 +4,8 @@ import "./components/ewt-dialog";
 import "./components/ewt-textfield";
 import "./components/ewt-button";
 import "./components/ewt-icon-button";
+import "./components/ewt-checkbox";
+import "./components/ewt-formfield";
 import "./components/ewt-circular-progress";
 import type { EwtTextfield } from "./components/ewt-textfield";
 import { Logger, Manifest, FlashStateType, FlashState } from "./const.js";
@@ -47,6 +49,7 @@ class EwtInstallDialog extends LitElement {
     | "DASHBOARD"
     | "PROVISION"
     | "INSTALL"
+    | "ASK_ERASE"
     | "LOGS" = "DASHBOARD";
 
   @state() private _installErase = false;
@@ -83,6 +86,8 @@ class EwtInstallDialog extends LitElement {
       }
     } else if (this._state === "INSTALL") {
       [heading, content, hideActions, allowClosing] = this._renderInstall();
+    } else if (this._state === "ASK_ERASE") {
+      [heading, content] = this._renderAskErase();
     } else if (this._state === "ERROR") {
       heading = "Error";
       content = this._renderMessage(ERROR_ICON, this._error!, true);
@@ -191,12 +196,15 @@ class EwtInstallDialog extends LitElement {
                   .label=${!this._isSameFirmware
                     ? `Install ${this._manifest!.name}`
                     : `Update ${this._manifest!.name}`}
-                  @click=${() =>
-                    this._startInstall(
-                      // Erase if manifest doens't force skipping it and it's not same firmware
-                      !this._manifest.new_install_skip_erase &&
-                        !this._isSameFirmware
-                    )}
+                  @click=${() => {
+                    if (this._isSameFirmware) {
+                      this._startInstall(false);
+                    } else if (this._manifest.new_install_prompt_erase) {
+                      this._state = "ASK_ERASE";
+                    } else {
+                      this._startInstall(true);
+                    }
+                  }}
                 ></ewt-button>
               </div>
             `
@@ -285,9 +293,14 @@ class EwtInstallDialog extends LitElement {
         <div>
           <ewt-button
             .label=${`Install ${this._manifest.name}`}
-            @click=${() =>
-              // Erase if manifest doens't force skipping it
-              this._startInstall(!this._manifest.new_install_skip_erase)}
+            @click=${() => {
+              if (this._manifest.new_install_prompt_erase) {
+                this._state = "ASK_ERASE";
+              } else {
+                // Default is to erase a device that does not support Improv Serial
+                this._startInstall(true);
+              }
+            }}
           ></ewt-button>
         </div>
 
@@ -415,7 +428,7 @@ class EwtInstallDialog extends LitElement {
         <ewt-button
           slot="primaryAction"
           label="Connect"
-          @click="${this._doProvision}"
+          @click=${this._doProvision}
         ></ewt-button>
         <ewt-button
           slot="secondaryAction"
@@ -430,6 +443,36 @@ class EwtInstallDialog extends LitElement {
     return [heading, content, hideActions];
   }
 
+  _renderAskErase(): [string | undefined, TemplateResult] {
+    const heading = "Erase device";
+    const content = html`
+      <div>
+        Do you want to erase the device before installing
+        ${this._manifest.name}? All data on the device will be lost.
+      </div>
+      <ewt-formfield label="Erase device" class="danger">
+        <ewt-checkbox></ewt-checkbox>
+      </ewt-formfield>
+      <ewt-button
+        slot="primaryAction"
+        label="Next"
+        @click=${() => {
+          const checkbox = this.shadowRoot!.querySelector("ewt-checkbox")!;
+          this._startInstall(checkbox.checked);
+        }}
+      ></ewt-button>
+      <ewt-button
+        slot="secondaryAction"
+        label="Back"
+        @click=${() => {
+          this._state = "DASHBOARD";
+        }}
+      ></ewt-button>
+    `;
+
+    return [heading, content];
+  }
+
   _renderInstall(): [string | undefined, TemplateResult, boolean, boolean] {
     let heading: string | undefined = `${
       this._installConfirmed ? "Installing" : "Install"
@@ -630,6 +673,14 @@ class EwtInstallDialog extends LitElement {
     this._manifest = await fetch(manifestURL).then(
       (resp): Promise<Manifest> => resp.json()
     );
+    if ("new_install_skip_erase" in this._manifest) {
+      console.warn(
+        'Manifest option "new_install_skip_erase" is deprecated. Use "new_install_prompt_erase" instead.'
+      );
+      if (this._manifest.new_install_skip_erase) {
+        this._manifest.new_install_prompt_erase = true;
+      }
+    }
   }
 
   private async _initialize(justErased = false) {
@@ -766,6 +817,10 @@ class EwtInstallDialog extends LitElement {
       --mdc-dialog-max-width: 390px;
       --mdc-theme-primary: var(--improv-primary-color, #03a9f4);
       --mdc-theme-on-primary: var(--improv-on-primary-color, #fff);
+      --improv-danger-color: #db4437;
+      --improv-text-color: rgba(0, 0, 0, 0.6);
+      --mdc-theme-text-primary-on-background: var(--improv-text-color);
+      --mdc-dialog-content-ink-color: var(--improv-text-color);
       text-align: left;
     }
     ewt-icon-button {
@@ -775,7 +830,7 @@ class EwtInstallDialog extends LitElement {
     }
     table {
       border-spacing: 0;
-      color: rgba(0, 0, 0, 0.6);
+      color: var(--improv-text-color);
       margin-bottom: 16px;
     }
     table svg {
@@ -817,10 +872,11 @@ class EwtInstallDialog extends LitElement {
       color: black;
     }
     .error {
-      color: #db4437;
+      color: var(--improv-danger-color);
     }
     .danger {
-      --mdc-theme-primary: #db4437;
+      --mdc-theme-primary: var(--improv-danger-color);
+      --mdc-theme-secondary: var(--improv-danger-color);
     }
     button.link {
       background: none;