diff --git a/CHANGELOG_LATEST.md b/CHANGELOG_LATEST.md index f212913d8..21d1c09f0 100644 --- a/CHANGELOG_LATEST.md +++ b/CHANGELOG_LATEST.md @@ -18,6 +18,7 @@ This release is based on the latest Espressif/Arduino core version 3. It brings - remove HA config for excluded entities [#3229](https://github.com/emsesp/EMS-ESP32/issues/3229) - `show system` reports the Ethernet status on boards with a PHY, so a failed initialisation or a link that never came up is visible instead of Ethernet being omitted completely. Also shows the loop task's lowest free stack, to help diagnose reboots - new Application Setting "Automatically check for firmware updates", off by default. When enabled EMS-ESP asks emsesp.org for the latest version once a day on its own, otherwise it only asks when the WebUI wants to show it, so an unattended system never contacts emsesp.org +- Automatically check and convert older 3.8.x filesystem to the new 3.9.0 format ## Fixed diff --git a/interface/package.json b/interface/package.json index e174c0392..d20b5736e 100644 --- a/interface/package.json +++ b/interface/package.json @@ -42,11 +42,11 @@ "@eslint/js": "^10.0.1", "@preact/preset-vite": "^2.10.6", "@trivago/prettier-plugin-sort-imports": "^6.0.2", - "@types/node": "^26.6.1", + "@types/node": "^26.6.2", "@types/react": "^19.3.0", "@types/react-dom": "^19.3.0", "concurrently": "^10.0.5", - "eslint": "^10.10.0", + "eslint": "^10.11.0", "eslint-config-prettier": "^10.1.8", "etag": "^1.8.1", "mime-types": "^3.0.2", diff --git a/interface/pnpm-lock.yaml b/interface/pnpm-lock.yaml index 394afc10a..5bd89b220 100644 --- a/interface/pnpm-lock.yaml +++ b/interface/pnpm-lock.yaml @@ -211,16 +211,16 @@ importers: devDependencies: '@eslint/js': specifier: ^10.0.1 - version: 10.0.1(eslint@10.10.0(supports-color@10.2.2)) + version: 10.0.1(eslint@10.11.0(supports-color@10.2.2)) '@preact/preset-vite': specifier: ^2.10.6 - version: 2.10.6(@babel/core@7.29.7(supports-color@10.2.2))(preact@10.29.8)(supports-color@10.2.2)(vite@8.3.0(@types/node@26.6.1)(terser@5.51.2)) + version: 2.10.6(@babel/core@7.29.7(supports-color@10.2.2))(preact@10.29.8)(supports-color@10.2.2)(vite@8.3.0(@types/node@26.6.2)(terser@5.51.2)) '@trivago/prettier-plugin-sort-imports': specifier: ^6.0.2 version: 6.0.2(prettier@3.9.8)(supports-color@10.2.2) '@types/node': - specifier: ^26.6.1 - version: 26.6.1 + specifier: ^26.6.2 + version: 26.6.2 '@types/react': specifier: ^19.3.0 version: 19.3.0 @@ -231,11 +231,11 @@ importers: specifier: ^10.0.5 version: 10.0.5 eslint: - specifier: ^10.10.0 - version: 10.10.0(supports-color@10.2.2) + specifier: ^10.11.0 + version: 10.11.0(supports-color@10.2.2) eslint-config-prettier: specifier: ^10.1.8 - version: 10.1.8(eslint@10.10.0(supports-color@10.2.2)) + version: 10.1.8(eslint@10.11.0(supports-color@10.2.2)) etag: specifier: ^1.8.1 version: 1.8.1 @@ -256,13 +256,13 @@ importers: version: 6.0.3 typescript-eslint: specifier: ^8.70.0 - version: 8.70.0(eslint@10.10.0(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3) + version: 8.70.0(eslint@10.11.0(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3) vite: specifier: ^8.3.0 - version: 8.3.0(@types/node@26.6.1)(terser@5.51.2) + version: 8.3.0(@types/node@26.6.2)(terser@5.51.2) vite-plugin-imagemin: specifier: ^0.6.1 - version: 0.6.1(supports-color@10.2.2)(vite@8.3.0(@types/node@26.6.1)(terser@5.51.2)) + version: 0.6.1(supports-color@10.2.2)(vite@8.3.0(@types/node@26.6.2)(terser@5.51.2)) packages: @@ -332,8 +332,8 @@ packages: resolution: {integrity: sha512-1k2lAGRMfHTcwuNYcCNUmaUffmQv8KWMfh2iJUUeRlwlwH4FdNG7mfPI10NPfLHJFThE4Tyr4mv7kTNZOiPuBg==} engines: {node: '>=6.9.0'} - '@babel/parser@7.29.8': - resolution: {integrity: sha512-E8lTAYNB1KW+FH+VGJuZM1ioAx2E6oVlvQFRrf5P8ZZmsiJXYAD9vTFV7yyEURNzgh1dFqMZuO6tUwcARbqFCA==} + '@babel/parser@7.29.9': + resolution: {integrity: sha512-CjXrNHTnvqBVqHgdBysY3vk2T8tpJHb5/RMeHJBTyVa9xgugCB0CJTx/3oO8RV2QRQP391RWpB7D6hLjm8V9uA==} engines: {node: '>=6.0.0'} hasBin: true @@ -843,8 +843,8 @@ packages: resolution: {integrity: sha512-zmPitbQ8+6zNutpwgcQuLcsEpn/Cj54Kbn7L5pX0Os5kdWplB7xPgEh/g+SWOB/qmows2gpuCaPyduq8ZZRnxA==} deprecated: This is a stub types definition. minimatch provides its own type definitions, so you do not need this installed. - '@types/node@26.6.1': - resolution: {integrity: sha512-VqGJBMCtdhqkBUCcBLvywI0NJ+KLuVzgNnlBUNFOQjqVxzo2lxLUNg1DSey8+u2u6ktswSAxg+s68QLzWHNOuA==} + '@types/node@26.6.2': + resolution: {integrity: sha512-X1P21scMv4zGKLYqjdGjaKa7COa0RKVYYZZN/NfvLQ1JegxFhdhpZG/Lyn8AXx6CDUavKAd11v6BvfpkDByK8g==} '@types/parse-json@4.0.2': resolution: {integrity: sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==} @@ -1339,8 +1339,8 @@ packages: duplexer3@0.1.5: resolution: {integrity: sha512-1A8za6ws41LQgv9HrE/66jyC5yuSjQ3L/KOpFtoBilsAK2iA2wuS5rTt1OCzIvtS2V7nVmedsUU+DGRcjBmOYA==} - electron-to-chromium@1.5.431: - resolution: {integrity: sha512-AAVihz2YwJeOdAynX8MUtqpvjY0gaiARp/7+r4kwqgzqXMrfG1qDN7vwT80uxAoOQRPcZH8t73EI+CA/1OJN8A==} + electron-to-chromium@1.5.433: + resolution: {integrity: sha512-5lCAbyZBjtmUt/RAGHRqrL2q0oEFRThDAsZHHDn9XHa89Qw7gMYOeSicBTy+AHfvo0r6vwsZvqNJTQIQy1BLzA==} emoji-regex@10.6.0: resolution: {integrity: sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==} @@ -1525,8 +1525,8 @@ packages: resolution: {integrity: sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==} engines: {node: ^20.19.0 || ^22.13.0 || >=24} - eslint@10.10.0: - resolution: {integrity: sha512-NPXn6r5zl4uET1DAVPaOwzX3rut4c0wcmw3dWJAfOsTM5+TogXo0DDjz8pwm/hL8cyVNpHqeK4JpN0NjnyFFNw==} + eslint@10.11.0: + resolution: {integrity: sha512-P7a6UEEqb9G95MYAtqkmsTbVXIYyzIfl6NGOIJk162PaahFxFyeGcrlXYFSiagECg4sEm8IseJdZBKR3rx6MsQ==} engines: {node: ^20.19.0 || ^22.13.0 || >=24} hasBin: true peerDependencies: @@ -3105,8 +3105,8 @@ packages: yaml: optional: true - which-typed-array@1.1.23: - resolution: {integrity: sha512-JMh8aK+1B/0bk/YNupICmH5MgCq6yNLKYQUfsZtSDLLCCmxUkHjUY+oOlIa90lO7lHN1woAfpenLdlKpXy9o+A==} + which-typed-array@1.1.24: + resolution: {integrity: sha512-wk4Mf4pR5mRP7eYuuTBCIQ9d0ud2Fv2jRLQpfgnRjbOxAFHmjKFValgTpitVKzJJS8ajnYQV2Du1SZ8j6b/EUQ==} engines: {node: '>= 0.4'} which@1.3.1: @@ -3197,7 +3197,7 @@ snapshots: '@babel/helper-compilation-targets': 7.29.7 '@babel/helper-module-transforms': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) '@babel/helpers': 7.29.7 - '@babel/parser': 7.29.8 + '@babel/parser': 7.29.9 '@babel/template': 7.29.7 '@babel/traverse': 7.29.8(supports-color@10.2.2) '@babel/types': 7.29.8 @@ -3212,7 +3212,7 @@ snapshots: '@babel/generator@7.29.8': dependencies: - '@babel/parser': 7.29.8 + '@babel/parser': 7.29.9 '@babel/types': 7.29.8 '@jridgewell/gen-mapping': 0.3.13 '@jridgewell/trace-mapping': 0.3.31 @@ -3261,7 +3261,7 @@ snapshots: '@babel/template': 7.29.7 '@babel/types': 7.29.8 - '@babel/parser@7.29.8': + '@babel/parser@7.29.9': dependencies: '@babel/types': 7.29.8 @@ -3293,7 +3293,7 @@ snapshots: '@babel/template@7.29.7': dependencies: '@babel/code-frame': 7.29.7 - '@babel/parser': 7.29.8 + '@babel/parser': 7.29.9 '@babel/types': 7.29.8 '@babel/traverse@7.29.8(supports-color@10.2.2)': @@ -3301,7 +3301,7 @@ snapshots: '@babel/code-frame': 7.29.7 '@babel/generator': 7.29.8 '@babel/helper-globals': 7.29.7 - '@babel/parser': 7.29.8 + '@babel/parser': 7.29.9 '@babel/template': 7.29.7 '@babel/types': 7.29.8 debug: 4.4.3(supports-color@10.2.2) @@ -3411,9 +3411,9 @@ snapshots: '@esbuild/linux-loong64@0.14.54': optional: true - '@eslint-community/eslint-utils@4.10.1(eslint@10.10.0(supports-color@10.2.2))': + '@eslint-community/eslint-utils@4.10.1(eslint@10.11.0(supports-color@10.2.2))': dependencies: - eslint: 10.10.0(supports-color@10.2.2) + eslint: 10.11.0(supports-color@10.2.2) eslint-visitor-keys: 3.4.3 '@eslint-community/regexpp@4.12.2': {} @@ -3434,9 +3434,9 @@ snapshots: dependencies: '@types/json-schema': 7.0.15 - '@eslint/js@10.0.1(eslint@10.10.0(supports-color@10.2.2))': + '@eslint/js@10.0.1(eslint@10.11.0(supports-color@10.2.2))': optionalDependencies: - eslint: 10.10.0(supports-color@10.2.2) + eslint: 10.11.0(supports-color@10.2.2) '@eslint/object-schema@3.0.5': {} @@ -3596,19 +3596,19 @@ snapshots: '@popperjs/core@2.11.8': {} - '@preact/preset-vite@2.10.6(@babel/core@7.29.7(supports-color@10.2.2))(preact@10.29.8)(supports-color@10.2.2)(vite@8.3.0(@types/node@26.6.1)(terser@5.51.2))': + '@preact/preset-vite@2.10.6(@babel/core@7.29.7(supports-color@10.2.2))(preact@10.29.8)(supports-color@10.2.2)(vite@8.3.0(@types/node@26.6.2)(terser@5.51.2))': dependencies: '@babel/core': 7.29.7(supports-color@10.2.2) '@babel/plugin-transform-react-jsx': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) '@babel/plugin-transform-react-jsx-development': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) - '@prefresh/vite': 2.4.12(preact@10.29.8)(supports-color@10.2.2)(vite@8.3.0(@types/node@26.6.1)(terser@5.51.2)) + '@prefresh/vite': 2.4.12(preact@10.29.8)(supports-color@10.2.2)(vite@8.3.0(@types/node@26.6.2)(terser@5.51.2)) '@rollup/pluginutils': 5.4.0 babel-plugin-transform-hook-names: 1.0.2(@babel/core@7.29.7(supports-color@10.2.2)) debug: 4.4.3(supports-color@10.2.2) magic-string: 0.30.21 picocolors: 1.1.1 - vite: 8.3.0(@types/node@26.6.1)(terser@5.51.2) - vite-prerender-plugin: 0.5.13(vite@8.3.0(@types/node@26.6.1)(terser@5.51.2)) + vite: 8.3.0(@types/node@26.6.2)(terser@5.51.2) + vite-prerender-plugin: 0.5.13(vite@8.3.0(@types/node@26.6.2)(terser@5.51.2)) zimmerframe: 1.1.5 transitivePeerDependencies: - preact @@ -3625,7 +3625,7 @@ snapshots: '@prefresh/utils@1.2.1': {} - '@prefresh/vite@2.4.12(preact@10.29.8)(supports-color@10.2.2)(vite@8.3.0(@types/node@26.6.1)(terser@5.51.2))': + '@prefresh/vite@2.4.12(preact@10.29.8)(supports-color@10.2.2)(vite@8.3.0(@types/node@26.6.2)(terser@5.51.2))': dependencies: '@babel/core': 7.29.7(supports-color@10.2.2) '@prefresh/babel-plugin': 0.5.4(@babel/core@7.29.7(supports-color@10.2.2)) @@ -3633,7 +3633,7 @@ snapshots: '@prefresh/utils': 1.2.1 '@rollup/pluginutils': 4.2.1 preact: 10.29.8 - vite: 8.3.0(@types/node@26.6.1)(terser@5.51.2) + vite: 8.3.0(@types/node@26.6.2)(terser@5.51.2) transitivePeerDependencies: - supports-color @@ -3711,7 +3711,7 @@ snapshots: '@trivago/prettier-plugin-sort-imports@6.0.2(prettier@3.9.8)(supports-color@10.2.2)': dependencies: '@babel/generator': 7.29.8 - '@babel/parser': 7.29.8 + '@babel/parser': 7.29.9 '@babel/traverse': 7.29.8(supports-color@10.2.2) '@babel/types': 7.29.8 javascript-natural-sort: 0.7.1 @@ -3729,7 +3729,7 @@ snapshots: '@types/glob@7.2.0': dependencies: '@types/minimatch': 6.0.0 - '@types/node': 26.6.1 + '@types/node': 26.6.2 '@types/imagemin-gifsicle@7.0.4': dependencies: @@ -3758,19 +3758,19 @@ snapshots: '@types/imagemin@7.0.1': dependencies: - '@types/node': 26.6.1 + '@types/node': 26.6.2 '@types/json-schema@7.0.15': {} '@types/keyv@3.1.4': dependencies: - '@types/node': 26.6.1 + '@types/node': 26.6.2 '@types/minimatch@6.0.0': dependencies: minimatch: 10.2.6 - '@types/node@26.6.1': + '@types/node@26.6.2': dependencies: undici-types: 8.9.0 @@ -3792,21 +3792,21 @@ snapshots: '@types/responselike@1.0.3': dependencies: - '@types/node': 26.6.1 + '@types/node': 26.6.2 '@types/svgo@2.6.4': dependencies: - '@types/node': 26.6.1 + '@types/node': 26.6.2 - '@typescript-eslint/eslint-plugin@8.70.0(@typescript-eslint/parser@8.70.0(eslint@10.10.0(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3))(eslint@10.10.0(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3)': + '@typescript-eslint/eslint-plugin@8.70.0(@typescript-eslint/parser@8.70.0(eslint@10.11.0(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3))(eslint@10.11.0(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3)': dependencies: '@eslint-community/regexpp': 4.12.2 - '@typescript-eslint/parser': 8.70.0(eslint@10.10.0(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3) + '@typescript-eslint/parser': 8.70.0(eslint@10.11.0(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3) '@typescript-eslint/scope-manager': 8.70.0 - '@typescript-eslint/type-utils': 8.70.0(eslint@10.10.0(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3) - '@typescript-eslint/utils': 8.70.0(eslint@10.10.0(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3) + '@typescript-eslint/type-utils': 8.70.0(eslint@10.11.0(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3) + '@typescript-eslint/utils': 8.70.0(eslint@10.11.0(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3) '@typescript-eslint/visitor-keys': 8.70.0 - eslint: 10.10.0(supports-color@10.2.2) + eslint: 10.11.0(supports-color@10.2.2) ignore: 7.0.9 natural-compare: 1.4.0 ts-api-utils: 2.5.0(typescript@6.0.3) @@ -3814,14 +3814,14 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/parser@8.70.0(eslint@10.10.0(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3)': + '@typescript-eslint/parser@8.70.0(eslint@10.11.0(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3)': dependencies: '@typescript-eslint/scope-manager': 8.70.0 '@typescript-eslint/types': 8.70.0 '@typescript-eslint/typescript-estree': 8.70.0(supports-color@10.2.2)(typescript@6.0.3) '@typescript-eslint/visitor-keys': 8.70.0 debug: 4.4.3(supports-color@10.2.2) - eslint: 10.10.0(supports-color@10.2.2) + eslint: 10.11.0(supports-color@10.2.2) typescript: 6.0.3 transitivePeerDependencies: - supports-color @@ -3844,13 +3844,13 @@ snapshots: dependencies: typescript: 6.0.3 - '@typescript-eslint/type-utils@8.70.0(eslint@10.10.0(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3)': + '@typescript-eslint/type-utils@8.70.0(eslint@10.11.0(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3)': dependencies: '@typescript-eslint/types': 8.70.0 '@typescript-eslint/typescript-estree': 8.70.0(supports-color@10.2.2)(typescript@6.0.3) - '@typescript-eslint/utils': 8.70.0(eslint@10.10.0(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3) + '@typescript-eslint/utils': 8.70.0(eslint@10.11.0(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3) debug: 4.4.3(supports-color@10.2.2) - eslint: 10.10.0(supports-color@10.2.2) + eslint: 10.11.0(supports-color@10.2.2) ts-api-utils: 2.5.0(typescript@6.0.3) typescript: 6.0.3 transitivePeerDependencies: @@ -3873,13 +3873,13 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/utils@8.70.0(eslint@10.10.0(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3)': + '@typescript-eslint/utils@8.70.0(eslint@10.11.0(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3)': dependencies: - '@eslint-community/eslint-utils': 4.10.1(eslint@10.10.0(supports-color@10.2.2)) + '@eslint-community/eslint-utils': 4.10.1(eslint@10.11.0(supports-color@10.2.2)) '@typescript-eslint/scope-manager': 8.70.0 '@typescript-eslint/types': 8.70.0 '@typescript-eslint/typescript-estree': 8.70.0(supports-color@10.2.2)(typescript@6.0.3) - eslint: 10.10.0(supports-color@10.2.2) + eslint: 10.11.0(supports-color@10.2.2) typescript: 6.0.3 transitivePeerDependencies: - supports-color @@ -4012,7 +4012,7 @@ snapshots: dependencies: baseline-browser-mapping: 2.11.25 caniuse-lite: 1.0.30001810 - electron-to-chromium: 1.5.431 + electron-to-chromium: 1.5.433 node-releases: 2.0.56 update-browserslist-db: 1.3.3(browserslist@4.29.0) @@ -4377,7 +4377,7 @@ snapshots: duplexer3@0.1.5: {} - electron-to-chromium@1.5.431: {} + electron-to-chromium@1.5.433: {} emoji-regex@10.6.0: {} @@ -4491,9 +4491,9 @@ snapshots: escape-string-regexp@4.0.0: {} - eslint-config-prettier@10.1.8(eslint@10.10.0(supports-color@10.2.2)): + eslint-config-prettier@10.1.8(eslint@10.11.0(supports-color@10.2.2)): dependencies: - eslint: 10.10.0(supports-color@10.2.2) + eslint: 10.11.0(supports-color@10.2.2) eslint-scope@9.1.2: dependencies: @@ -4506,9 +4506,9 @@ snapshots: eslint-visitor-keys@5.0.1: {} - eslint@10.10.0(supports-color@10.2.2): + eslint@10.11.0(supports-color@10.2.2): dependencies: - '@eslint-community/eslint-utils': 4.10.1(eslint@10.10.0(supports-color@10.2.2)) + '@eslint-community/eslint-utils': 4.10.1(eslint@10.11.0(supports-color@10.2.2)) '@eslint-community/regexpp': 4.12.2 '@eslint/config-array': 0.23.5(supports-color@10.2.2) '@eslint/config-helpers': 0.7.0 @@ -5058,7 +5058,7 @@ snapshots: is-typed-array@1.1.15: dependencies: - which-typed-array: 1.1.23 + which-typed-array: 1.1.24 is-utf8@0.2.1: {} @@ -5940,13 +5940,13 @@ snapshots: dependencies: typescript: 6.0.3 - typescript-eslint@8.70.0(eslint@10.10.0(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3): + typescript-eslint@8.70.0(eslint@10.11.0(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3): dependencies: - '@typescript-eslint/eslint-plugin': 8.70.0(@typescript-eslint/parser@8.70.0(eslint@10.10.0(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3))(eslint@10.10.0(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3) - '@typescript-eslint/parser': 8.70.0(eslint@10.10.0(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3) + '@typescript-eslint/eslint-plugin': 8.70.0(@typescript-eslint/parser@8.70.0(eslint@10.11.0(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3))(eslint@10.11.0(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3) + '@typescript-eslint/parser': 8.70.0(eslint@10.11.0(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3) '@typescript-eslint/typescript-estree': 8.70.0(supports-color@10.2.2)(typescript@6.0.3) - '@typescript-eslint/utils': 8.70.0(eslint@10.10.0(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3) - eslint: 10.10.0(supports-color@10.2.2) + '@typescript-eslint/utils': 8.70.0(eslint@10.11.0(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3) + eslint: 10.11.0(supports-color@10.2.2) typescript: 6.0.3 transitivePeerDependencies: - supports-color @@ -5991,7 +5991,7 @@ snapshots: spdx-correct: 3.2.0 spdx-expression-parse: 3.0.1 - vite-plugin-imagemin@0.6.1(supports-color@10.2.2)(vite@8.3.0(@types/node@26.6.1)(terser@5.51.2)): + vite-plugin-imagemin@0.6.1(supports-color@10.2.2)(vite@8.3.0(@types/node@26.6.2)(terser@5.51.2)): dependencies: '@types/imagemin': 7.0.1 '@types/imagemin-gifsicle': 7.0.4 @@ -6016,11 +6016,11 @@ snapshots: imagemin-webp: 6.1.0 jpegtran-bin: 6.0.1 pathe: 0.2.0 - vite: 8.3.0(@types/node@26.6.1)(terser@5.51.2) + vite: 8.3.0(@types/node@26.6.2)(terser@5.51.2) transitivePeerDependencies: - supports-color - vite-prerender-plugin@0.5.13(vite@8.3.0(@types/node@26.6.1)(terser@5.51.2)): + vite-prerender-plugin@0.5.13(vite@8.3.0(@types/node@26.6.2)(terser@5.51.2)): dependencies: kolorist: 1.8.0 magic-string: 0.30.21 @@ -6028,9 +6028,9 @@ snapshots: simple-code-frame: 1.3.0 source-map: 0.7.6 stack-trace: 1.0.0 - vite: 8.3.0(@types/node@26.6.1)(terser@5.51.2) + vite: 8.3.0(@types/node@26.6.2)(terser@5.51.2) - vite@8.3.0(@types/node@26.6.1)(terser@5.51.2): + vite@8.3.0(@types/node@26.6.2)(terser@5.51.2): dependencies: lightningcss: 1.33.0 picomatch: 4.0.7 @@ -6038,11 +6038,11 @@ snapshots: rolldown: 1.2.9 tinyglobby: 0.2.17 optionalDependencies: - '@types/node': 26.6.1 + '@types/node': 26.6.2 fsevents: 2.3.3 terser: 5.51.2 - which-typed-array@1.1.23: + which-typed-array@1.1.24: dependencies: available-typed-arrays: 1.0.7 call-bind: 1.0.9 diff --git a/interface/src/app/settings/Version.tsx b/interface/src/app/settings/Version.tsx index f54203f9c..e45a1c46c 100644 --- a/interface/src/app/settings/Version.tsx +++ b/interface/src/app/settings/Version.tsx @@ -297,16 +297,12 @@ const InstallDialog = memo( )} {upgradeImportantMessageType === 2 && LL.UPGRADE_IMPORTANT_MESSAGES_2()} - {upgradeImportantMessageType === 1 && ( - <> - {LL.UPGRADE_IMPORTANT_MESSAGES_1()} - - - {LL.DOWNLOAD_SYSTEM_BACKUP()} - - - - )} + {upgradeImportantMessageType === 1 && LL.UPGRADE_IMPORTANT_MESSAGES_1()} + + + {LL.DOWNLOAD_SYSTEM_BACKUP()} + + =6.9.0'} - '@babel/parser@7.29.8': - resolution: {integrity: sha512-E8lTAYNB1KW+FH+VGJuZM1ioAx2E6oVlvQFRrf5P8ZZmsiJXYAD9vTFV7yyEURNzgh1dFqMZuO6tUwcARbqFCA==} + '@babel/parser@7.29.9': + resolution: {integrity: sha512-CjXrNHTnvqBVqHgdBysY3vk2T8tpJHb5/RMeHJBTyVa9xgugCB0CJTx/3oO8RV2QRQP391RWpB7D6hLjm8V9uA==} engines: {node: '>=6.0.0'} hasBin: true @@ -318,7 +318,7 @@ snapshots: '@babel/generator@7.29.8': dependencies: - '@babel/parser': 7.29.8 + '@babel/parser': 7.29.9 '@babel/types': 7.29.8 '@jridgewell/gen-mapping': 0.3.13 '@jridgewell/trace-mapping': 0.3.31 @@ -330,14 +330,14 @@ snapshots: '@babel/helper-validator-identifier@7.29.7': {} - '@babel/parser@7.29.8': + '@babel/parser@7.29.9': dependencies: '@babel/types': 7.29.8 '@babel/template@7.29.7': dependencies: '@babel/code-frame': 7.29.7 - '@babel/parser': 7.29.8 + '@babel/parser': 7.29.9 '@babel/types': 7.29.8 '@babel/traverse@7.29.8': @@ -345,7 +345,7 @@ snapshots: '@babel/code-frame': 7.29.7 '@babel/generator': 7.29.8 '@babel/helper-globals': 7.29.7 - '@babel/parser': 7.29.8 + '@babel/parser': 7.29.9 '@babel/template': 7.29.7 '@babel/types': 7.29.8 debug: 4.4.3 @@ -376,7 +376,7 @@ snapshots: '@trivago/prettier-plugin-sort-imports@6.0.2(prettier@3.9.8)': dependencies: '@babel/generator': 7.29.8 - '@babel/parser': 7.29.8 + '@babel/parser': 7.29.9 '@babel/traverse': 7.29.8 '@babel/types': 7.29.8 javascript-natural-sort: 0.7.1 diff --git a/mock-api/restServer.ts b/mock-api/restServer.ts index b561e486d..071b070f2 100644 --- a/mock-api/restServer.ts +++ b/mock-api/restServer.ts @@ -98,13 +98,13 @@ let system_status = { wifi_rssi: -41, esp_platform: 'ESP32', build_flags: 'DEMO', - cpu_type: 'ESP32', + cpu_type: 'ESP32-D0WD-V3', cpu_rev: 0, cpu_cores: 2, cpu_freq_mhz: 240, max_alloc_heap: 191, - arduino_version: 'ESP32 Arduino v2.0.17', - sdk_version: 'v4.4.7', + arduino_version: 'Tasmota Arduino v3.3.8', + sdk_version: '5.5.4.260407', partition: 'app0', flash_chip_size: 16384, flash_chip_speed: 80000000, @@ -127,13 +127,13 @@ let system_status = { }, { partition: 'app1', - version: '3.8.1-dev.40', + version: '3.8.3', install_date: '2025-03-01T13:29:13.999Z', size: 4672 }, { partition: 'factory', - version: '3.8.1-dev.39', + version: '3.8.4', install_date: '2025-03-01T13:29:13.999Z', size: 4672 } @@ -141,24 +141,22 @@ let system_status = { // partitions: [], developer_mode: settings.developer_mode, disable_reset: settings.disable_reset, - model: '', - board: '', - // model: 'BBQKees Electronics EMS Gateway E32 V2 (E32 V2.0 P3/2024011)', - // board: 'E32V2', - // status: 0, - status: 3 + model: 'BBQKees Electronics E32V2.2 rev.2.2/2024074', + board: 'E32V2_2', + status: 0 + // status: 3 }; -// Test Versioning +// Test the versioning let DEV_VERSION_IS_UPGRADEABLE: boolean; let STABLE_VERSION_IS_UPGRADEABLE: boolean; let THIS_VERSION: string; -let LATEST_STABLE_VERSION = '3.8.3'; +let LATEST_STABLE_VERSION = '3.8.4'; let LATEST_DEV_VERSION = '3.9.0-dev.1'; // scenarios for testing versioning -// let version_test = 0; // on latest stable, or switch to dev -let version_test = 1; // on latest dev, or switch back to stable +let version_test = 0; // on latest stable, or switch to dev +// let version_test = 1; // on latest dev, or switch back to stable // let version_test = 2; // upgrade an older stable to latest stable or switch to latest dev // let version_test = 3; // upgrade dev to latest, or switch to stable // let version_test = 4; // downgrade to an older dev, or switch back to stable @@ -446,25 +444,24 @@ function executeCommand(name: string) { } // called by Action endpoint upgradeImportantMessages +// 0 is do nothing +// 1 is a special upgrade and needs an export->factory reset->import +// 2 is a major upgrade function upgradeImportantMessages(version: string) { - // 0 is do nothing - // 1 means 3.9 and factory reset required - // 2 means a major version upgrade - let upgradeImportantMessageType_n = 0; - // check file extensions + // check file extensions first if (version.endsWith('.md5') || version.endsWith('.json')) { upgradeImportantMessageType_n = 0; // digest / backup restore: no upgrade warning } else if (version.endsWith('.bin')) { // extract the version number from the filename and if its going from 3.8.x to 3.9.x, then set upgradeImportantMessageType_n to 1 const versionNumber = version.split('.'); if (versionNumber[0] === '3' && versionNumber[1] === '8') { - upgradeImportantMessageType_n = 1; // make it 1, factory reset required + upgradeImportantMessageType_n = 2; // major upgrade } } else { - // this is a version string like "3.9.0" - upgradeImportantMessageType_n = 1; // make it 1, for testing, meaning a factory reset is required + // the parameter is a version string like "3.9.0" + upgradeImportantMessageType_n = 2; // major upgrade } console.log( diff --git a/pio_local.ini_example b/pio_local.ini_example index 29a40d240..c1bd4f59d 100644 --- a/pio_local.ini_example +++ b/pio_local.ini_example @@ -41,7 +41,8 @@ custom_password = admin ; ** upload settings ** ; for direct USB, which is the default you can set an optional port for example: ; upload_port = /dev/ttyUSB0 or COM3 -; +; upload_speed = 921600 + ; for direct via HTTP (OTA) set: ; upload_protocol = custom ; custom_emsesp_ip = or ems-esp.local diff --git a/project-words.txt b/project-words.txt index 703d511a7..0e8f7a52e 100644 --- a/project-words.txt +++ b/project-words.txt @@ -6,9 +6,9 @@ abluftqualität abluftqualitätsfühler abrt absburnpow -ACAC -ACAH -ACAO +acac +acah +acao accomodate aceotrope activehigh @@ -48,6 +48,7 @@ appused apssid apsta arduhal +arduinoespressif arduinojson arduinoota arjan @@ -70,6 +71,7 @@ ausreichend authmode autodst automode +autonegotiation autoscroll auxelecheatnrgcons auxelecheatnrgconsheating @@ -139,6 +141,7 @@ bytetime calinttemp catagory cbuf +ccrc cerapur cfamily changeloglevel @@ -289,6 +292,7 @@ dewoffset dewpoint dewtemp dewtemperature +dhcpc dhwmintemp dhwprio dhwvalve @@ -362,6 +366,7 @@ entitypath enumupdate enviline equipt +eraseap erct erdwärmetauscher errordisp @@ -458,6 +463,7 @@ gpoc greenstar grokhotkov haclimate +handshaked hardwarestatus harwerth headersize @@ -501,7 +507,7 @@ heatsource heatsources heatstarts heattemp -heattransfer +heattransfer heattransfersystem heatup heatvalve @@ -578,6 +584,7 @@ iconua idedata iele ignwork +iile iiwi ijoy imlhd @@ -615,6 +622,7 @@ itty ivtclock jtag junkersclock +juststopped kamin karlesky keelbypass @@ -706,6 +714,7 @@ mbap mbar mbed mbedtsl +mbit mdio mdns melis @@ -744,7 +753,7 @@ mlongcalls mmio mmode mmplus -mockServer +mockserver modbus modee modetype @@ -762,6 +771,7 @@ msgstr msys mtext mult +mwdt mysys nachheiz nachheizreg @@ -780,6 +790,7 @@ neopixel netcmp netflowtemp netif +netifs newdatetime newnow newpcb @@ -836,6 +847,7 @@ oilpreheat oldoffset olimex olimexpoe +olimexpoew omea onchange onetimekey @@ -843,6 +855,7 @@ onewire ooops operatingstate opmode +optimisations optipng osrc outdoorhold @@ -877,7 +890,7 @@ poolsettemp poolshunt poolshuntstatus poolstarts -pooltemp +pooltemp portx powerreduction powervalues @@ -944,7 +957,7 @@ rawstr rcplus readback readelf -READYCLIENT +readyclient readymail recp recved @@ -1000,6 +1013,7 @@ roomsettings roomtemp roomtempdiff roomtemperature +roomtempoffset roomthermostat roomthermostats rrts @@ -1029,6 +1043,7 @@ seltemp seltemplow seltempoff seltempsingle +sendmail sendtype sensordata sensorfails @@ -1037,6 +1052,7 @@ sensorname sensorreads sensortype seperator +serialises serverclass servicecode servicecodenumber @@ -1087,11 +1103,13 @@ soll speicher spidqs spiio +spiram squaremeters srts staconnected stadisconnected staipassigned +startm startshp starttemp starttemperature @@ -1112,6 +1130,7 @@ substract summermode summersetmode summertemp +sumr supchan suprapur svenska @@ -1161,6 +1180,7 @@ tempparmode temprature tempsensor tempstatus +testemail testtemperature thermaldisinfect thermostate @@ -1271,6 +1291,7 @@ werror wextra whenmodeoff wificlient +wifioff wifireconnects wifistrength winsock @@ -1281,6 +1302,7 @@ workm workmin worktime wouldblock +wrover wswitch wtype wunused @@ -1330,24 +1352,3 @@ zyxwvutsrqponmlkjihgfedcba öffnen česky živanović -MWDT -juststopped -handshaked -startm -netifs -testemail -sendmail -serialises -SPIRAM -optimisations -IILE -Sumr -eraseap -roomtempoffset -dhcpc -wifioff -autonegotiation -Mbit -WROVER -OLIMEXPOEW -arduinoespressif \ No newline at end of file diff --git a/src/core/console.cpp b/src/core/console.cpp index c6ff9dcb5..60ca00a20 100644 --- a/src/core/console.cpp +++ b/src/core/console.cpp @@ -319,7 +319,7 @@ static void setup_commands(std::shared_ptr const & commands) { settings.eth_clock_mode = data[8]; return StateUpdateResult::CHANGED; }); - shell.printfln("Loaded board profile %s. Restarting...", board_profile.c_str()); + shell.printfln("Set board profile to %s. Restarting...", board_profile.c_str()); EMSESP::system_.system_restart(); }); diff --git a/src/core/emsesp.cpp b/src/core/emsesp.cpp index a43dd2b23..e0d5eb3d5 100644 --- a/src/core/emsesp.cpp +++ b/src/core/emsesp.cpp @@ -21,6 +21,7 @@ #ifndef EMSESP_STANDALONE #include "esp_ota_ops.h" #include "rom/rtc.h" +#include "littlefs_compat.h" #endif static_assert(uuid::thread_safe, "uuid-common must be thread-safe"); @@ -1723,22 +1724,26 @@ void EMSESP::start() { // start the file system #ifndef EMSESP_STANDALONE -#if defined(EMSESP_DEBUG) - // don't format the filesystem in debug mode if (!LittleFS.begin(false)) { - LOG_ERROR("LittleFS Mount Failed. Attempting to convert..."); - return; - } else { - // it mounted, show the contents of the root directory - LOG_DEBUG("Listing root directory before:"); - system_.listDir("/", 3); - } -#else - // format filesystem if there isn't one - if (!LittleFS.begin(true)) { - LOG_ERROR("LittleFS Mount Failed"); - return; + // A partition written by an Arduino Core 2 build is readable but records limits + // this build refuses to mount. Rewrite the superblock and retry before resorting + // to a format, which would throw away the user's settings. + if (littlefs_migrate() && LittleFS.begin(false)) { + LOG_NOTICE("Converted Filesystem to new format"); + } else if (LittleFS.begin(true)) { + LOG_WARNING("Formatted Filesystem, any previous settings have been lost"); + } else { + // if we reach here, nothing will ever work so go into a infinite loop + Serial.println("LittleFS Mount Failed. Cannot start EMS-ESP"); + while (true) { + delay(1000); + yield(); + } + } } +#if defined(EMSESP_DEBUG) + LOG_DEBUG("Listing root directory before:"); + system_.listDir("/", 3); #endif #endif diff --git a/src/core/littlefs_compat.cpp b/src/core/littlefs_compat.cpp new file mode 100644 index 000000000..54985a53f --- /dev/null +++ b/src/core/littlefs_compat.cpp @@ -0,0 +1,282 @@ +/* + * EMS-ESP - https://github.com/emsesp/EMS-ESP + * Copyright 2020-2025 emsesp.org + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +// A LittleFS partition written by an Arduino Core 2 build will not mount under the +// esp_littlefs that ships with Core 3, even though every byte of it is readable. Two +// fields of the superblock are compatibility gates rather than real format differences, +// and lfs_mount() rejects the filesystem on either one: +// +// name_max Core 2 used the upstream default of 255, while esp_littlefs derives 63 from +// CONFIG_LITTLEFS_OBJ_NAME_LEN and refuses anything larger. Storing 0 means +// "use the library's own limit", which is what a Core 3 format writes. +// version Core 2 recorded v2.1 and this build writes v2.0, and a higher minor is +// refused. The library here understands everything a v2.1 image holds, so +// lowering the field is enough. +// +// Rewriting them means walking the metadata block by hand. A block holds a revision count +// followed by a chain of commits, each one a run of tags terminated by a CCRC tag and a +// CRC covering everything that commit wrote. Tags are stored big endian and XORed with the +// preceding tag, so the chain has to be replayed from the start to find anything. The +// superblock struct is not at a fixed offset either: on a filesystem that has been written +// to, directory entries are committed ahead of it. + +#ifndef EMSESP_STANDALONE + +#include "littlefs_compat.h" + +#include +#include +#include + +#include +#include + +namespace emsesp { + +namespace { + +#ifndef CONFIG_LITTLEFS_OBJ_NAME_LEN +#define CONFIG_LITTLEFS_OBJ_NAME_LEN 64 +#endif +constexpr uint32_t NAME_MAX_SUPPORTED = CONFIG_LITTLEFS_OBJ_NAME_LEN - 1; +constexpr uint32_t DISK_VERSION_TARGET = 0x00020000; + +// littlefs block size on the ESP32, which is the flash sector size +constexpr uint32_t BLOCK_SIZE = 4096; + +// byte offsets within lfs_superblock_t, all little endian uint32_t +constexpr uint32_t SB_VERSION = 0; +constexpr uint32_t SB_BLOCK_SIZE = 4; +constexpr uint32_t SB_NAME_MAX = 12; +constexpr uint32_t SB_SIZE = 24; + +// tag accessors, mirroring lfs.c +constexpr uint32_t TYPE_INLINESTRUCT = 0x201; +constexpr uint32_t TYPE_CCRC = 0x500; + +constexpr bool tag_isvalid(uint32_t t) { + return (t & 0x80000000) == 0; +} +constexpr uint32_t tag_type2(uint32_t t) { + return (t & 0x78000000) >> 20; +} +constexpr uint32_t tag_type3(uint32_t t) { + return (t & 0x7ff00000) >> 20; +} +constexpr uint32_t tag_chunk(uint32_t t) { + return (t & 0x0ff00000) >> 20; +} +constexpr uint32_t tag_id(uint32_t t) { + return (t & 0x000ffc00) >> 10; +} +constexpr uint32_t tag_size(uint32_t t) { + return t & 0x3ff; +} +// a size of 0x3ff marks a delete tag, which carries no payload +constexpr uint32_t tag_dsize(uint32_t t) { + return 4 + (tag_size(t) == 0x3ff ? 0 : tag_size(t)); +} + +uint32_t rd_be32(const uint8_t * p) { + return (static_cast(p[0]) << 24) | (static_cast(p[1]) << 16) | (static_cast(p[2]) << 8) | p[3]; +} +uint32_t rd_le32(const uint8_t * p) { + return (static_cast(p[3]) << 24) | (static_cast(p[2]) << 16) | (static_cast(p[1]) << 8) | p[0]; +} +void wr_le32(uint8_t * p, uint32_t v) { + p[0] = static_cast(v); + p[1] = static_cast(v >> 8); + p[2] = static_cast(v >> 16); + p[3] = static_cast(v >> 24); +} + +// the CRC-32 littlefs commits with: reflected 0x04C11DB7, seeded 0xffffffff, no final xor +uint32_t lfs_crc(uint32_t crc, const void * buffer, size_t size) { + static const uint32_t rtable[16] = { + 0x00000000, + 0x1db71064, + 0x3b6e20c8, + 0x26d930ac, + 0x76dc4190, + 0x6b6b51f4, + 0x4db26158, + 0x5005713c, + 0xedb88320, + 0xf00f9344, + 0xd6d6a3e8, + 0xcb61b38c, + 0x9b64c2b0, + 0x86d3d2d4, + 0xa00ae278, + 0xbdbdf21c, + }; + const uint8_t * data = static_cast(buffer); + for (size_t i = 0; i < size; i++) { + crc = (crc >> 4) ^ rtable[(crc ^ data[i]) & 0xf]; + crc = (crc >> 4) ^ rtable[(crc ^ (data[i] >> 4)) & 0xf]; + } + return crc; +} + +// where the superblock lives, and which bytes the CRC of its commit covers +struct Located { + bool found = false; + uint32_t struct_off = 0; // start of the 24 byte lfs_superblock_t + uint32_t commit_start = 0; // first byte the commit's CRC covers + uint32_t crc_off = 0; // where that CRC is stored +}; + +// Replays a metadata block's commit chain the same way lfs_dir_fetchmatch() does, keeping +// the last superblock struct that belongs to a commit whose CRC verifies. Reporting +// nothing until a stored CRC matches means a successful scan also proves we are reading +// the tag stream correctly, so we never write back a block we misunderstood. +Located locate_superblock(const uint8_t * buf) { + Located found; + uint32_t off = 0; + uint32_t ptag = 0xffffffff; + uint32_t commit_start = 0; + uint32_t crc = lfs_crc(0xffffffff, buf, 4); // the revision count leads the first commit + bool pending = false; + uint32_t pending_off = 0; + + while (true) { + off += tag_dsize(ptag); + if (off + 4 > BLOCK_SIZE) { + break; + } + + crc = lfs_crc(crc, &buf[off], 4); + const uint32_t tag = rd_be32(&buf[off]) ^ ptag; + + if (!tag_isvalid(tag)) { + break; // nothing committed beyond this point + } + if (off + tag_dsize(tag) > BLOCK_SIZE) { + break; + } + ptag = tag; + + if (tag_type2(tag) == TYPE_CCRC) { + if (crc != rd_le32(&buf[off + 4])) { + break; // commit never completed, so the chain ends here + } + if (pending) { + found.found = true; + found.struct_off = pending_off; + found.commit_start = commit_start; + found.crc_off = off + 4; + pending = false; + } + ptag ^= (tag_chunk(tag) & 1u) << 31; + crc = 0xffffffff; + commit_start = off + tag_dsize(tag); // the padding after the CRC is not covered + continue; + } + + crc = lfs_crc(crc, &buf[off + 4], tag_dsize(tag) - 4); + + if (tag_type3(tag) == TYPE_INLINESTRUCT && tag_id(tag) == 0 && tag_size(tag) == SB_SIZE) { + pending = true; + pending_off = off + 4; + } + } + + return found; +} + +// resolve the same partition esp_littlefs would pick +const esp_partition_t * find_partition(const char * partition_label) { + if (partition_label != nullptr) { + return esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_ANY, partition_label); + } + // 0x83 is the littlefs subtype, 0x82 the spiffs one that older partition tables use + const esp_partition_t * part = esp_partition_find_first(ESP_PARTITION_TYPE_DATA, static_cast(0x83), nullptr); + if (part != nullptr) { + return part; + } + return esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_SPIFFS, nullptr); +} + +bool patch_block(const esp_partition_t * part, uint32_t block, uint8_t * buf) { + const uint32_t addr = block * BLOCK_SIZE; + + if (esp_partition_read(part, addr, buf, BLOCK_SIZE) != ESP_OK) { + return false; + } + + const Located sb = locate_superblock(buf); + if (!sb.found) { + return false; + } + + uint8_t * s = buf + sb.struct_off; + if (rd_le32(s + SB_BLOCK_SIZE) != BLOCK_SIZE) { + return false; // geometry we did not expect means we are not reading a superblock + } + + bool changed = false; + + if (rd_le32(s + SB_NAME_MAX) > NAME_MAX_SUPPORTED) { + wr_le32(s + SB_NAME_MAX, 0); + changed = true; + } + + const uint32_t version = rd_le32(s + SB_VERSION); + if (version > DISK_VERSION_TARGET && (version >> 16) == (DISK_VERSION_TARGET >> 16)) { + wr_le32(s + SB_VERSION, DISK_VERSION_TARGET); + changed = true; + } + + if (!changed) { + return false; + } + + wr_le32(buf + sb.crc_off, lfs_crc(0xffffffff, buf + sb.commit_start, sb.crc_off - sb.commit_start)); + + return esp_partition_erase_range(part, addr, BLOCK_SIZE) == ESP_OK && esp_partition_write(part, addr, buf, BLOCK_SIZE) == ESP_OK; +} + +} // namespace + +bool littlefs_migrate(const char * partition_label) { + const esp_partition_t * part = find_partition(partition_label); + if (part == nullptr) { + return false; + } + + uint8_t * buf = static_cast(malloc(BLOCK_SIZE)); + if (buf == nullptr) { + return false; + } + + // Blocks 0 and 1 are the root metadata pair. Rewriting them one at a time keeps one + // readable copy available for littlefs to fall back on if we lose power mid-write. + bool patched = false; + for (uint32_t block = 0; block < 2; block++) { + if (patch_block(part, block, buf)) { + patched = true; + } + } + + free(buf); + return patched; +} + +} // namespace emsesp + +#endif diff --git a/src/core/littlefs_compat.h b/src/core/littlefs_compat.h new file mode 100644 index 000000000..29df7ab03 --- /dev/null +++ b/src/core/littlefs_compat.h @@ -0,0 +1,31 @@ +/* + * EMS-ESP - https://github.com/emsesp/EMS-ESP + * Copyright 2020-2025 emsesp.org + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef EMSESP_LITTLEFS_COMPAT_H_ +#define EMSESP_LITTLEFS_COMPAT_H_ + +namespace emsesp { + +// Rewrites the superblock of a LittleFS partition created by an Arduino Core 2 build so +// that this build's littlefs will mount it. Only call once a mount has already failed. +// Returns true if flash was changed, meaning another mount attempt is worth making. +bool littlefs_migrate(const char * partition_label = nullptr); + +} // namespace emsesp + +#endif diff --git a/src/core/system.cpp b/src/core/system.cpp index b867e8642..5a637ffe3 100644 --- a/src/core/system.cpp +++ b/src/core/system.cpp @@ -476,7 +476,8 @@ void System::get_partition_info() { auto current_partition = (const char *)esp_ota_get_running_partition()->label; // update the current version and partition name in NVS if not already set - if (EMSESP::nvs_.getString(current_partition) != EMSESP_APP_VERSION || emsesp::EMSESP::nvs_.getBool(emsesp::EMSESP_NVS_BOOT_NEW_FIRMWARE, true)) { + if (!EMSESP::nvs_.isKey(current_partition) || EMSESP::nvs_.getString(current_partition) != EMSESP_APP_VERSION + || emsesp::EMSESP::nvs_.getBool(emsesp::EMSESP_NVS_BOOT_NEW_FIRMWARE, true)) { EMSESP::nvs_.putBool(emsesp::EMSESP_NVS_BOOT_NEW_FIRMWARE, false); EMSESP::nvs_.putString(current_partition, EMSESP_APP_VERSION); char c[20]; @@ -508,7 +509,9 @@ void System::get_partition_info() { if (is_valid) { PartitionInfo p_info; // if there is an entry for this partition in NVS, get it's version from NVS - p_info.version = EMSESP::nvs_.getString(part->label, "").c_str(); + if (EMSESP::nvs_.isKey(part->label)) { + p_info.version = EMSESP::nvs_.getString(part->label).c_str(); + } char c[20]; snprintf(c, sizeof(c), "d_%s", (const char *)part->label); time_t d = EMSESP::nvs_.getULong(c, 0); diff --git a/src/core/temperaturesensor.cpp b/src/core/temperaturesensor.cpp index 4fd053ae6..aa812cf18 100644 --- a/src/core/temperaturesensor.cpp +++ b/src/core/temperaturesensor.cpp @@ -199,7 +199,7 @@ void TemperatureSensor::loop() { if (firstscan_ > 0 && set_internal_) { set_internal_ = false; Sensor * s = &sensors_[0]; - if (firstscan_ > 1) { + if (firstscan_ > 1 && EMSESP::nvs_.isKey("intTemp")) { std::string s_nvs = EMSESP::nvs_.getString("intTemp").c_str(); for (uint8_t i = 0; i < firstscan_; i++) { if (s_nvs == sensors_[i].id()) { diff --git a/src/web/WebSettingsService.cpp b/src/web/WebSettingsService.cpp index 1ae7388ae..6787a7148 100644 --- a/src/web/WebSettingsService.cpp +++ b/src/web/WebSettingsService.cpp @@ -465,7 +465,7 @@ void WebSettings::set_board_profile(WebSettings & settings) { String bbq_board = EMSESP::system_.getBBQKeesGatewayDetails(FUSE_VALUE::BOARD); if (!bbq_board.isEmpty() && settings.board_profile != "CUSTOM") { #if defined(EMSESP_DEBUG) - EMSESP::logger().info("Overriding board profile with eFuse value %s", bbq_board.c_str()); + EMSESP::logger().debug("Overriding board profile with eFuse value %s", bbq_board.c_str()); #endif settings.board_profile = bbq_board; } @@ -532,9 +532,9 @@ void WebSettings::set_board_profile(WebSettings & settings) { #ifndef EMSESP_STANDALONE uint32_t psram_size = ESP.getPsramSize() / 1024; // in KB if (psram_size > 0) { - EMSESP::logger().info("Loaded board profile %s (PSRAM: %lu KB)", settings.board_profile.c_str(), psram_size); + EMSESP::logger().info("Applied board profile %s (PSRAM: %lu KB)", settings.board_profile.c_str(), psram_size); } else { - EMSESP::logger().info("Loaded board profile %s (PSRAM: not available)", settings.board_profile.c_str()); + EMSESP::logger().info("Applied board profile %s (PSRAM: not available)", settings.board_profile.c_str()); } #endif diff --git a/src/web/WebStatusService.cpp b/src/web/WebStatusService.cpp index 69d50103a..804e7bdff 100644 --- a/src/web/WebStatusService.cpp +++ b/src/web/WebStatusService.cpp @@ -258,9 +258,9 @@ void WebStatusService::action(AsyncWebServerRequest * request, JsonVariant json) // action = upgradeImportantMessages // returns the type of upgrade important message to display in the UI -// 0 = no message (if just a minor version upgrade) -// 1 = going from <= 3.8 to 3.9 (has new partition layout) -// 2 = major version upgrade +// 0 = no message (if just a minor version upgrade) +// 1 = special message for upgrades (no longer used) +// 2 = major version upgrade // version can be like 3.8.2 or a filename like EMS-ESP-3_8_2-dev_13-ESP32-16MB+.bin uint8_t WebStatusService::upgradeImportantMessages(std::string & version) { if (version.empty()) { @@ -307,9 +307,10 @@ uint8_t WebStatusService::upgradeImportantMessages(std::string & version) { return 0; // no upgrade (same version or downgrade) } - if (current_version < FirmwareVersion("3.9.0-dev.0") && latest_version.major() == 3 && latest_version.minor() == 9) { - return 1; // upgrading to 3.9.x from anything older - new partition layout warning - } + // case "1" is no longer used + // if (current_version < FirmwareVersion("3.9.0-dev.0") && latest_version.major() == 3 && latest_version.minor() == 9) { + // return 1; // upgrading to 3.9.x from anything older - new partition layout warning + // } if (current_version.major() < latest_version.major()) { return 2; // major version upgrade