mirror of
https://github.com/home-assistant/frontend.git
synced 2025-08-11 18:29:27 +00:00
Compare commits
1 Commits
data-disk-
...
exclude-vi
Author | SHA1 | Date | |
---|---|---|---|
![]() |
0ec7973e95 |
@@ -1,10 +1,9 @@
|
||||
{
|
||||
"extends": [
|
||||
"airbnb-base",
|
||||
"airbnb-typescript/base",
|
||||
"plugin:@typescript-eslint/recommended",
|
||||
"plugin:wc/recommended",
|
||||
"plugin:lit/all",
|
||||
"plugin:lit/recommended",
|
||||
"prettier"
|
||||
],
|
||||
"parser": "@typescript-eslint/parser",
|
||||
@@ -36,51 +35,55 @@
|
||||
"es6": true
|
||||
},
|
||||
"rules": {
|
||||
"class-methods-use-this": "off",
|
||||
"new-cap": "off",
|
||||
"prefer-template": "off",
|
||||
"object-shorthand": "off",
|
||||
"func-names": "off",
|
||||
"no-underscore-dangle": "off",
|
||||
"strict": "off",
|
||||
"no-plusplus": "off",
|
||||
"no-bitwise": "error",
|
||||
"comma-dangle": "off",
|
||||
"vars-on-top": "off",
|
||||
"no-continue": "off",
|
||||
"no-param-reassign": "off",
|
||||
"no-multi-assign": "off",
|
||||
"no-console": "error",
|
||||
"radix": "off",
|
||||
"no-alert": "off",
|
||||
"no-nested-ternary": "off",
|
||||
"prefer-destructuring": "off",
|
||||
"class-methods-use-this": 0,
|
||||
"new-cap": 0,
|
||||
"prefer-template": 0,
|
||||
"object-shorthand": 0,
|
||||
"func-names": 0,
|
||||
"prefer-arrow-callback": 0,
|
||||
"no-underscore-dangle": 0,
|
||||
"strict": 0,
|
||||
"prefer-spread": 0,
|
||||
"no-plusplus": 0,
|
||||
"no-bitwise": 2,
|
||||
"comma-dangle": 0,
|
||||
"vars-on-top": 0,
|
||||
"no-continue": 0,
|
||||
"no-param-reassign": 0,
|
||||
"no-multi-assign": 0,
|
||||
"no-console": 2,
|
||||
"radix": 0,
|
||||
"no-alert": 0,
|
||||
"no-return-await": 0,
|
||||
"no-nested-ternary": 0,
|
||||
"prefer-destructuring": 0,
|
||||
"no-restricted-globals": [2, "event"],
|
||||
"prefer-promise-reject-errors": "off",
|
||||
"import/prefer-default-export": "off",
|
||||
"import/no-default-export": "off",
|
||||
"import/no-unresolved": "off",
|
||||
"import/no-cycle": "off",
|
||||
"prefer-promise-reject-errors": 0,
|
||||
"import/order": 0,
|
||||
"import/prefer-default-export": 0,
|
||||
"import/no-unresolved": 0,
|
||||
"import/no-cycle": 0,
|
||||
"import/extensions": [
|
||||
"error",
|
||||
2,
|
||||
"ignorePackages",
|
||||
{ "ts": "never", "js": "never" }
|
||||
],
|
||||
"no-restricted-syntax": ["error", "LabeledStatement", "WithStatement"],
|
||||
"object-curly-newline": "off",
|
||||
"default-case": "off",
|
||||
"wc/no-self-class": "off",
|
||||
"no-shadow": "off",
|
||||
"@typescript-eslint/camelcase": "off",
|
||||
"@typescript-eslint/ban-ts-comment": "off",
|
||||
"@typescript-eslint/no-use-before-define": "off",
|
||||
"@typescript-eslint/no-non-null-assertion": "off",
|
||||
"@typescript-eslint/no-explicit-any": "off",
|
||||
"@typescript-eslint/explicit-function-return-type": "off",
|
||||
"@typescript-eslint/explicit-module-boundary-types": "off",
|
||||
"object-curly-newline": 0,
|
||||
"default-case": 0,
|
||||
"wc/no-self-class": 0,
|
||||
"no-shadow": 0,
|
||||
"@typescript-eslint/camelcase": 0,
|
||||
"@typescript-eslint/ban-ts-comment": 0,
|
||||
"@typescript-eslint/no-use-before-define": 0,
|
||||
"@typescript-eslint/no-non-null-assertion": 0,
|
||||
"@typescript-eslint/no-explicit-any": 0,
|
||||
"@typescript-eslint/no-unused-vars": 0,
|
||||
"@typescript-eslint/explicit-function-return-type": 0,
|
||||
"@typescript-eslint/explicit-module-boundary-types": 0,
|
||||
"@typescript-eslint/no-shadow": ["error"],
|
||||
"@typescript-eslint/naming-convention": [
|
||||
"off",
|
||||
0,
|
||||
{
|
||||
"selector": "default",
|
||||
"format": ["camelCase", "snake_case"],
|
||||
@@ -98,22 +101,8 @@
|
||||
"format": ["PascalCase"]
|
||||
}
|
||||
],
|
||||
"@typescript-eslint/no-unused-vars": "off",
|
||||
"unused-imports/no-unused-vars": [
|
||||
"error",
|
||||
{
|
||||
"vars": "all",
|
||||
"varsIgnorePattern": "^_",
|
||||
"args": "after-used",
|
||||
"argsIgnorePattern": "^_",
|
||||
"ignoreRestSiblings": true
|
||||
}
|
||||
],
|
||||
"unused-imports/no-unused-imports": "error",
|
||||
"lit/attribute-value-entities": "off",
|
||||
"lit/no-template-map": "off",
|
||||
"lit/no-template-arrow": "warn"
|
||||
"lit/attribute-value-entities": 0
|
||||
},
|
||||
"plugins": ["disable", "unused-imports"],
|
||||
"plugins": ["disable", "import", "lit", "prettier", "@typescript-eslint"],
|
||||
"processor": "disable/disable"
|
||||
}
|
||||
|
78
.github/workflows/ci.yaml
vendored
78
.github/workflows/ci.yaml
vendored
@@ -10,21 +10,26 @@ on:
|
||||
- dev
|
||||
- master
|
||||
|
||||
env:
|
||||
NODE_VERSION: 14
|
||||
NODE_OPTIONS: --max_old_space_size=6144
|
||||
|
||||
jobs:
|
||||
lint:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check out files from GitHub
|
||||
uses: actions/checkout@v2
|
||||
- name: Set up Node ${{ env.NODE_VERSION }}
|
||||
uses: actions/setup-node@v2
|
||||
- name: Setting up Node.js
|
||||
uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: ${{ env.NODE_VERSION }}
|
||||
cache: yarn
|
||||
node-version: 12.x
|
||||
- name: Get yarn cache path
|
||||
id: yarn-cache-dir-path
|
||||
run: echo "::set-output name=dir::$(yarn cache dir)"
|
||||
- name: Fetching Yarn cache
|
||||
uses: actions/cache@v1
|
||||
with:
|
||||
path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
|
||||
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-yarn-
|
||||
- name: Install dependencies
|
||||
run: yarn install
|
||||
env:
|
||||
@@ -37,35 +42,51 @@ jobs:
|
||||
run: yarn run lint:types
|
||||
- name: Run prettier
|
||||
run: yarn run lint:prettier
|
||||
- name: Check for duplicate dependencies
|
||||
run: yarn dedupe --check
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check out files from GitHub
|
||||
uses: actions/checkout@v2
|
||||
- name: Set up Node ${{ env.NODE_VERSION }}
|
||||
uses: actions/setup-node@v2
|
||||
- name: Setting up Node.js
|
||||
uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: ${{ env.NODE_VERSION }}
|
||||
cache: yarn
|
||||
node-version: 12.x
|
||||
- name: Get yarn cache path
|
||||
id: yarn-cache-dir-path
|
||||
run: echo "::set-output name=dir::$(yarn cache dir)"
|
||||
- name: Fetching Yarn cache
|
||||
uses: actions/cache@v1
|
||||
with:
|
||||
path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
|
||||
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-yarn-
|
||||
- name: Install dependencies
|
||||
run: yarn install
|
||||
env:
|
||||
CI: true
|
||||
- name: Run Tests
|
||||
run: yarn run test
|
||||
- name: Run Mocha
|
||||
run: npm run mocha
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
needs: [lint, test]
|
||||
steps:
|
||||
- name: Check out files from GitHub
|
||||
uses: actions/checkout@v2
|
||||
- name: Set up Node ${{ env.NODE_VERSION }}
|
||||
uses: actions/setup-node@v2
|
||||
- name: Setting up Node.js
|
||||
uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: ${{ env.NODE_VERSION }}
|
||||
cache: yarn
|
||||
node-version: 12.x
|
||||
- name: Get yarn cache path
|
||||
id: yarn-cache-dir-path
|
||||
run: echo "::set-output name=dir::$(yarn cache dir)"
|
||||
- name: Fetching Yarn cache
|
||||
uses: actions/cache@v1
|
||||
with:
|
||||
path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
|
||||
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-yarn-
|
||||
- name: Install dependencies
|
||||
run: yarn install
|
||||
env:
|
||||
@@ -80,11 +101,20 @@ jobs:
|
||||
steps:
|
||||
- name: Check out files from GitHub
|
||||
uses: actions/checkout@v2
|
||||
- name: Set up Node ${{ env.NODE_VERSION }}
|
||||
uses: actions/setup-node@v2
|
||||
- name: Setting up Node.js
|
||||
uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: ${{ env.NODE_VERSION }}
|
||||
cache: yarn
|
||||
node-version: 12.x
|
||||
- name: Get yarn cache path
|
||||
id: yarn-cache-dir-path
|
||||
run: echo "::set-output name=dir::$(yarn cache dir)"
|
||||
- name: Fetching Yarn cache
|
||||
uses: actions/cache@v1
|
||||
with:
|
||||
path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
|
||||
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-yarn-
|
||||
- name: Install dependencies
|
||||
run: yarn install
|
||||
env:
|
||||
|
22
.github/workflows/demo.yaml
vendored
22
.github/workflows/demo.yaml
vendored
@@ -4,22 +4,26 @@ on:
|
||||
push:
|
||||
branches:
|
||||
- dev
|
||||
|
||||
env:
|
||||
NODE_VERSION: 14
|
||||
NODE_OPTIONS: --max_old_space_size=6144
|
||||
|
||||
jobs:
|
||||
deploy:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check out files from GitHub
|
||||
uses: actions/checkout@v2
|
||||
- name: Set up Node ${{ env.NODE_VERSION }}
|
||||
uses: actions/setup-node@v2
|
||||
- name: Setting up Node.js
|
||||
uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: ${{ env.NODE_VERSION }}
|
||||
cache: yarn
|
||||
node-version: 12.x
|
||||
- name: Get yarn cache path
|
||||
id: yarn-cache-dir-path
|
||||
run: echo "::set-output name=dir::$(yarn cache dir)"
|
||||
- name: Fetching Yarn cache
|
||||
uses: actions/cache@v1
|
||||
with:
|
||||
path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
|
||||
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-yarn-
|
||||
- name: Install dependencies
|
||||
run: yarn install
|
||||
env:
|
||||
|
16
.github/workflows/release.yaml
vendored
16
.github/workflows/release.yaml
vendored
@@ -6,9 +6,9 @@ on:
|
||||
- published
|
||||
|
||||
env:
|
||||
WHEELS_TAG: 3.8-alpine3.12
|
||||
PYTHON_VERSION: 3.8
|
||||
NODE_VERSION: 14
|
||||
NODE_OPTIONS: --max_old_space_size=6144
|
||||
NODE_VERSION: 12.1
|
||||
|
||||
jobs:
|
||||
release:
|
||||
@@ -30,15 +30,7 @@ jobs:
|
||||
uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: ${{ env.NODE_VERSION }}
|
||||
cache: yarn
|
||||
|
||||
- name: Install dependencies
|
||||
run: yarn install
|
||||
|
||||
- name: Download Translations
|
||||
run: ./script/translations_download
|
||||
env:
|
||||
LOKALISE_TOKEN: ${{ secrets.LOKALISE_TOKEN }}
|
||||
- name: Build and release package
|
||||
run: |
|
||||
python3 -m pip install twine
|
||||
@@ -72,8 +64,6 @@ jobs:
|
||||
strategy:
|
||||
matrix:
|
||||
arch: ["aarch64", "armhf", "armv7", "amd64", "i386"]
|
||||
tag:
|
||||
- "3.9-alpine3.14"
|
||||
steps:
|
||||
- name: Download requirements.txt
|
||||
uses: actions/download-artifact@v2
|
||||
@@ -83,7 +73,7 @@ jobs:
|
||||
- name: Build wheels
|
||||
uses: home-assistant/wheels@master
|
||||
with:
|
||||
tag: ${{ matrix.tag }}
|
||||
tag: ${{ env.WHEELS_TAG }}
|
||||
arch: ${{ matrix.arch }}
|
||||
wheels-host: ${{ secrets.WHEELS_HOST }}
|
||||
wheels-key: ${{ secrets.WHEELS_KEY }}
|
||||
|
42
.github/workflows/translations.yaml
vendored
42
.github/workflows/translations.yaml
vendored
@@ -1,6 +1,8 @@
|
||||
name: Translations
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: "30 0 * * *"
|
||||
push:
|
||||
branches:
|
||||
- dev
|
||||
@@ -8,7 +10,7 @@ on:
|
||||
- src/translations/en.json
|
||||
|
||||
env:
|
||||
NODE_VERSION: 14
|
||||
NODE_VERSION: 12
|
||||
|
||||
jobs:
|
||||
upload:
|
||||
@@ -18,8 +20,46 @@ jobs:
|
||||
- name: Checkout the repository
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Set up Node ${{ env.NODE_VERSION }}
|
||||
uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: ${{ env.NODE_VERSION }}
|
||||
|
||||
- name: Upload Translations
|
||||
run: |
|
||||
export LOKALISE_TOKEN="${{ secrets.LOKALISE_TOKEN }}"
|
||||
|
||||
./script/translations_upload_base
|
||||
|
||||
download:
|
||||
name: Download
|
||||
needs: upload
|
||||
if: github.event_name == 'schedule'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout the repository
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Set up Node ${{ env.NODE_VERSION }}
|
||||
uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: ${{ env.NODE_VERSION }}
|
||||
|
||||
- name: Download Translations
|
||||
run: |
|
||||
export LOKALISE_TOKEN="${{ secrets.LOKALISE_TOKEN }}"
|
||||
|
||||
npm install
|
||||
./script/translations_download
|
||||
|
||||
- name: Initialize git
|
||||
uses: home-assistant/actions/helpers/git-init@master
|
||||
with:
|
||||
name: GitHub Action
|
||||
email: github-action@users.noreply.github.com
|
||||
|
||||
- name: Update translation
|
||||
run: |
|
||||
git add translations
|
||||
git commit -am "Translation update"
|
||||
git push
|
||||
|
27
.gitignore
vendored
27
.gitignore
vendored
@@ -1,23 +1,10 @@
|
||||
.DS_Store
|
||||
.reify-cache
|
||||
|
||||
# build
|
||||
build
|
||||
build-translations/*
|
||||
hass_frontend/*
|
||||
dist
|
||||
|
||||
# yarn
|
||||
.yarn/*
|
||||
!.yarn/patches
|
||||
!.yarn/releases
|
||||
!.yarn/plugins
|
||||
!.yarn/sdks
|
||||
!.yarn/versions
|
||||
.pnp.*
|
||||
node_modules/*
|
||||
yarn-error.log
|
||||
npm-debug.log
|
||||
.DS_Store
|
||||
hass_frontend/*
|
||||
.reify-cache
|
||||
|
||||
# Python stuff
|
||||
*.py[cod]
|
||||
@@ -27,8 +14,11 @@ npm-debug.log
|
||||
# venv stuff
|
||||
pyvenv.cfg
|
||||
pip-selfcheck.json
|
||||
venv/*
|
||||
venv
|
||||
.venv
|
||||
lib
|
||||
bin
|
||||
dist
|
||||
|
||||
# vscode
|
||||
.vscode/*
|
||||
@@ -41,8 +31,9 @@ src/cast/dev_const.ts
|
||||
|
||||
# Secrets
|
||||
.lokalise_token
|
||||
yarn-error.log
|
||||
|
||||
# asdf
|
||||
#asdf
|
||||
.tool-versions
|
||||
|
||||
# Home Assistant config
|
||||
|
4
.mocharc.cjs
Normal file
4
.mocharc.cjs
Normal file
@@ -0,0 +1,4 @@
|
||||
module.exports = {
|
||||
require: "test-mocha/testconf.js",
|
||||
timeout: 10000,
|
||||
};
|
File diff suppressed because one or more lines are too long
@@ -1,29 +0,0 @@
|
||||
diff --git a/lib/uni-virtualizer/lib/polyfillLoaders/EventTarget.js b/lib/uni-virtualizer/lib/polyfillLoaders/EventTarget.js
|
||||
index d92179f7fd5315203f870a6963e871dc8ddf6c0c..362e284121b97e0fba0925225777aebc32e26b8d 100644
|
||||
--- a/lib/uni-virtualizer/lib/polyfillLoaders/EventTarget.js
|
||||
+++ b/lib/uni-virtualizer/lib/polyfillLoaders/EventTarget.js
|
||||
@@ -1,14 +1,15 @@
|
||||
-let _ET, ET;
|
||||
+let _ET;
|
||||
+let ET;
|
||||
export default async function EventTarget() {
|
||||
- return ET || init();
|
||||
+ return ET || init();
|
||||
}
|
||||
async function init() {
|
||||
- _ET = window.EventTarget;
|
||||
- try {
|
||||
- new _ET();
|
||||
- }
|
||||
- catch (_a) {
|
||||
- _ET = (await import('event-target-shim')).EventTarget;
|
||||
- }
|
||||
- return (ET = _ET);
|
||||
+ _ET = window.EventTarget;
|
||||
+ try {
|
||||
+ new _ET();
|
||||
+ } catch (_a) {
|
||||
+ _ET = (await import("event-target-shim")).default.EventTarget;
|
||||
+ }
|
||||
+ return (ET = _ET);
|
||||
}
|
@@ -1,34 +0,0 @@
|
||||
diff --git a/lib/legacy/class.js b/lib/legacy/class.js
|
||||
index aee2511be1cd9bf900ee552bc98190c1631c57c0..f2f499d68bf52034cac9c28307c99e8ce6b8417d 100644
|
||||
--- a/lib/legacy/class.js
|
||||
+++ b/lib/legacy/class.js
|
||||
@@ -304,17 +304,23 @@ function GenerateClassFromInfo(info, Base, behaviors) {
|
||||
// only proceed if the generated class' prototype has not been registered.
|
||||
const generatedProto = PolymerGenerated.prototype;
|
||||
if (!generatedProto.hasOwnProperty(JSCompiler_renameProperty('__hasRegisterFinished', generatedProto))) {
|
||||
- generatedProto.__hasRegisterFinished = true;
|
||||
+ // make sure legacy lifecycle is called on the *element*'s prototype
|
||||
+ // and not the generated class prototype; if the element has been
|
||||
+ // extended, these are *not* the same.
|
||||
+ const proto = Object.getPrototypeOf(this);
|
||||
+ // Only set flag when generated prototype itself is registered,
|
||||
+ // as this element may be extended from, and needs to run `registered`
|
||||
+ // on all behaviors on the subclass as well.
|
||||
+ if (proto === generatedProto) {
|
||||
+ generatedProto.__hasRegisterFinished = true;
|
||||
+ }
|
||||
// ensure superclass is registered first.
|
||||
super._registered();
|
||||
// copy properties onto the generated class lazily if we're optimizing,
|
||||
- if (legacyOptimizations) {
|
||||
+ if (legacyOptimizations && !Object.hasOwnProperty(generatedProto, '__hasCopiedProperties')) {
|
||||
+ generatedProto.__hasCopiedProperties = true;
|
||||
copyPropertiesToProto(generatedProto);
|
||||
}
|
||||
- // make sure legacy lifecycle is called on the *element*'s prototype
|
||||
- // and not the generated class prototype; if the element has been
|
||||
- // extended, these are *not* the same.
|
||||
- const proto = Object.getPrototypeOf(this);
|
||||
let list = lifecycle.beforeRegister;
|
||||
if (list) {
|
||||
for (let i=0; i < list.length; i++) {
|
File diff suppressed because one or more lines are too long
8
.yarn/plugins/@yarnpkg/plugin-typescript.cjs
vendored
8
.yarn/plugins/@yarnpkg/plugin-typescript.cjs
vendored
File diff suppressed because one or more lines are too long
631
.yarn/releases/yarn-3.0.2.cjs
vendored
631
.yarn/releases/yarn-3.0.2.cjs
vendored
File diff suppressed because one or more lines are too long
@@ -1,9 +0,0 @@
|
||||
nodeLinker: node-modules
|
||||
|
||||
plugins:
|
||||
- path: .yarn/plugins/@yarnpkg/plugin-typescript.cjs
|
||||
spec: "@yarnpkg/plugin-typescript"
|
||||
- path: .yarn/plugins/@yarnpkg/plugin-interactive-tools.cjs
|
||||
spec: "@yarnpkg/plugin-interactive-tools"
|
||||
|
||||
yarnPath: .yarn/releases/yarn-3.0.2.cjs
|
@@ -1,170 +0,0 @@
|
||||
/* eslint-disable @typescript-eslint/no-var-requires */
|
||||
const path = require("path");
|
||||
|
||||
// Currently only supports CommonJS modules, as require is synchronous. `import` would need babel running asynchronous.
|
||||
module.exports = function inlineConstants(babel, options, cwd) {
|
||||
const t = babel.types;
|
||||
|
||||
if (!Array.isArray(options.modules)) {
|
||||
throw new TypeError(
|
||||
"babel-plugin-inline-constants: expected a `modules` array to be passed"
|
||||
);
|
||||
}
|
||||
|
||||
if (options.resolveExtensions && !Array.isArray(options.resolveExtensions)) {
|
||||
throw new TypeError(
|
||||
"babel-plugin-inline-constants: expected `resolveExtensions` to be an array"
|
||||
);
|
||||
}
|
||||
|
||||
const ignoreModuleNotFound = options.ignoreModuleNotFound;
|
||||
const resolveExtensions = options.resolveExtensions;
|
||||
|
||||
const hasRelativeModules = options.modules.some(
|
||||
(module) => module.startsWith(".") || module.startsWith("/")
|
||||
);
|
||||
|
||||
const modules = Object.fromEntries(
|
||||
options.modules.map((module) => {
|
||||
const absolute = module.startsWith(".")
|
||||
? require.resolve(module, { paths: [cwd] })
|
||||
: module;
|
||||
// eslint-disable-next-line import/no-dynamic-require
|
||||
return [absolute, require(absolute)];
|
||||
})
|
||||
);
|
||||
|
||||
const toLiteral = (value) => {
|
||||
if (typeof value === "string") {
|
||||
return t.stringLiteral(value);
|
||||
}
|
||||
|
||||
if (typeof value === "number") {
|
||||
return t.numericLiteral(value);
|
||||
}
|
||||
|
||||
if (typeof value === "boolean") {
|
||||
return t.booleanLiteral(value);
|
||||
}
|
||||
|
||||
if (value === null) {
|
||||
return t.nullLiteral();
|
||||
}
|
||||
|
||||
throw new Error(
|
||||
"babel-plugin-inline-constants: cannot handle non-literal `" + value + "`"
|
||||
);
|
||||
};
|
||||
|
||||
const resolveAbsolute = (value, state, resolveExtensionIndex) => {
|
||||
if (!state.filename) {
|
||||
throw new TypeError(
|
||||
"babel-plugin-inline-constants: expected a `filename` to be set for files"
|
||||
);
|
||||
}
|
||||
|
||||
if (resolveExtensions && resolveExtensionIndex !== undefined) {
|
||||
value += resolveExtensions[resolveExtensionIndex];
|
||||
}
|
||||
|
||||
try {
|
||||
return require.resolve(value, { paths: [path.dirname(state.filename)] });
|
||||
} catch (error) {
|
||||
if (
|
||||
error.code === "MODULE_NOT_FOUND" &&
|
||||
resolveExtensions &&
|
||||
(resolveExtensionIndex === undefined ||
|
||||
resolveExtensionIndex < resolveExtensions.length - 1)
|
||||
) {
|
||||
const resolveExtensionIdx = (resolveExtensionIndex || -1) + 1;
|
||||
return resolveAbsolute(value, state, resolveExtensionIdx);
|
||||
}
|
||||
|
||||
if (error.code === "MODULE_NOT_FOUND" && ignoreModuleNotFound) {
|
||||
return undefined;
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
const importDeclaration = (p, state) => {
|
||||
if (p.node.type !== "ImportDeclaration") {
|
||||
return;
|
||||
}
|
||||
const absolute =
|
||||
hasRelativeModules && p.node.source.value.startsWith(".")
|
||||
? resolveAbsolute(p.node.source.value, state)
|
||||
: p.node.source.value;
|
||||
|
||||
if (!absolute || !(absolute in modules)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const module = modules[absolute];
|
||||
|
||||
for (const specifier of p.node.specifiers) {
|
||||
if (
|
||||
specifier.type === "ImportDefaultSpecifier" &&
|
||||
specifier.local &&
|
||||
specifier.local.type === "Identifier"
|
||||
) {
|
||||
if (!("default" in module)) {
|
||||
throw new Error(
|
||||
"babel-plugin-inline-constants: cannot access default export from `" +
|
||||
p.node.source.value +
|
||||
"`"
|
||||
);
|
||||
}
|
||||
|
||||
const variableValue = toLiteral(module.default);
|
||||
const variable = t.variableDeclarator(
|
||||
t.identifier(specifier.local.name),
|
||||
variableValue
|
||||
);
|
||||
|
||||
p.insertBefore({
|
||||
type: "VariableDeclaration",
|
||||
kind: "const",
|
||||
declarations: [variable],
|
||||
});
|
||||
} else if (
|
||||
specifier.type === "ImportSpecifier" &&
|
||||
specifier.imported &&
|
||||
specifier.imported.type === "Identifier" &&
|
||||
specifier.local &&
|
||||
specifier.local.type === "Identifier"
|
||||
) {
|
||||
if (!(specifier.imported.name in module)) {
|
||||
throw new Error(
|
||||
"babel-plugin-inline-constants: cannot access `" +
|
||||
specifier.imported.name +
|
||||
"` from `" +
|
||||
p.node.source.value +
|
||||
"`"
|
||||
);
|
||||
}
|
||||
|
||||
const variableValue = toLiteral(module[specifier.imported.name]);
|
||||
const variable = t.variableDeclarator(
|
||||
t.identifier(specifier.local.name),
|
||||
variableValue
|
||||
);
|
||||
|
||||
p.insertBefore({
|
||||
type: "VariableDeclaration",
|
||||
kind: "const",
|
||||
declarations: [variable],
|
||||
});
|
||||
} else {
|
||||
throw new Error("Cannot handle specifier `" + specifier.type + "`");
|
||||
}
|
||||
}
|
||||
p.remove();
|
||||
};
|
||||
|
||||
return {
|
||||
visitor: {
|
||||
ImportDeclaration: importDeclaration,
|
||||
},
|
||||
};
|
||||
};
|
@@ -5,6 +5,8 @@ const paths = require("./paths.js");
|
||||
|
||||
// Files from NPM Packages that should not be imported
|
||||
module.exports.ignorePackages = ({ latestBuild }) => [
|
||||
// Bloats bundle and it's not used.
|
||||
path.resolve(require.resolve("moment"), "../locale"),
|
||||
// Part of yaml.js and only used for !!js functions that we don't use
|
||||
require.resolve("esprima"),
|
||||
];
|
||||
@@ -18,8 +20,7 @@ module.exports.emptyPackages = ({ latestBuild }) =>
|
||||
require.resolve("@polymer/paper-styles/default-theme.js"),
|
||||
// Loads stuff from a CDN
|
||||
require.resolve("@polymer/font-roboto/roboto.js"),
|
||||
require.resolve("@vaadin/vaadin-material-styles/typography.js"),
|
||||
require.resolve("@vaadin/vaadin-material-styles/font-icons.js"),
|
||||
require.resolve("@vaadin/vaadin-material-styles/font-roboto.js"),
|
||||
// Compatibility not needed for latest builds
|
||||
latestBuild &&
|
||||
// wrapped in require.resolve so it blows up if file no longer exists
|
||||
@@ -51,29 +52,17 @@ module.exports.terserOptions = (latestBuild) => ({
|
||||
|
||||
module.exports.babelOptions = ({ latestBuild }) => ({
|
||||
babelrc: false,
|
||||
compact: false,
|
||||
presets: [
|
||||
!latestBuild && [
|
||||
"@babel/preset-env",
|
||||
{
|
||||
useBuiltIns: "entry",
|
||||
corejs: "3.15",
|
||||
bugfixes: true,
|
||||
corejs: "3.6",
|
||||
},
|
||||
],
|
||||
"@babel/preset-typescript",
|
||||
].filter(Boolean),
|
||||
plugins: [
|
||||
[
|
||||
path.resolve(
|
||||
paths.polymer_dir,
|
||||
"build-scripts/babel-plugins/inline-constants-plugin.js"
|
||||
),
|
||||
{
|
||||
modules: ["@mdi/js"],
|
||||
ignoreModuleNotFound: true,
|
||||
},
|
||||
],
|
||||
// Part of ES2018. Converts {...a, b: 2} to Object.assign({}, a, {b: 2})
|
||||
!latestBuild && [
|
||||
"@babel/plugin-proposal-object-rest-spread",
|
||||
@@ -82,21 +71,21 @@ module.exports.babelOptions = ({ latestBuild }) => ({
|
||||
// Only support the syntax, Webpack will handle it.
|
||||
"@babel/plugin-syntax-import-meta",
|
||||
"@babel/plugin-syntax-dynamic-import",
|
||||
"@babel/plugin-syntax-top-level-await",
|
||||
"@babel/plugin-proposal-optional-chaining",
|
||||
"@babel/plugin-proposal-nullish-coalescing-operator",
|
||||
["@babel/plugin-proposal-decorators", { decoratorsBeforeExport: true }],
|
||||
["@babel/plugin-proposal-private-methods", { loose: true }],
|
||||
["@babel/plugin-proposal-private-property-in-object", { loose: true }],
|
||||
["@babel/plugin-proposal-class-properties", { loose: true }],
|
||||
].filter(Boolean),
|
||||
exclude: [
|
||||
// \\ for Windows, / for Mac OS and Linux
|
||||
/node_modules[\\/]core-js/,
|
||||
/node_modules[\\/]webpack[\\/]buildin/,
|
||||
],
|
||||
});
|
||||
|
||||
// Are already ES5, cause warnings when babelified.
|
||||
module.exports.babelExclude = () => [
|
||||
require.resolve("@mdi/js/mdi.js"),
|
||||
require.resolve("hls.js"),
|
||||
require.resolve("vis-network/peer/esm/vis-network.js"),
|
||||
];
|
||||
|
||||
const outputPath = (outputRoot, latestBuild) =>
|
||||
path.resolve(outputRoot, latestBuild ? "frontend_latest" : "frontend_es5");
|
||||
|
||||
|
@@ -47,8 +47,8 @@ gulp.task(
|
||||
gulp.parallel("gen-icons-json", "build-translations"),
|
||||
"copy-static-app",
|
||||
env.useRollup() ? "rollup-prod-app" : "webpack-prod-app",
|
||||
// Don't compress running tests
|
||||
...(env.isTest() ? [] : ["compress-app"]),
|
||||
...// Don't compress running tests
|
||||
(env.isTest() ? [] : ["compress-app"]),
|
||||
gulp.parallel(
|
||||
"gen-pages-prod",
|
||||
"gen-index-app-prod",
|
||||
|
@@ -5,32 +5,32 @@ require("./translations");
|
||||
|
||||
gulp.task(
|
||||
"clean",
|
||||
gulp.parallel("clean-translations", () =>
|
||||
del([paths.app_output_root, paths.build_dir])
|
||||
)
|
||||
gulp.parallel("clean-translations", function cleanOutputAndBuildDir() {
|
||||
return del([paths.app_output_root, paths.build_dir]);
|
||||
})
|
||||
);
|
||||
|
||||
gulp.task(
|
||||
"clean-demo",
|
||||
gulp.parallel("clean-translations", () =>
|
||||
del([paths.demo_output_root, paths.build_dir])
|
||||
)
|
||||
gulp.parallel("clean-translations", function cleanOutputAndBuildDir() {
|
||||
return del([paths.demo_output_root, paths.build_dir]);
|
||||
})
|
||||
);
|
||||
|
||||
gulp.task(
|
||||
"clean-cast",
|
||||
gulp.parallel("clean-translations", () =>
|
||||
del([paths.cast_output_root, paths.build_dir])
|
||||
)
|
||||
gulp.parallel("clean-translations", function cleanOutputAndBuildDir() {
|
||||
return del([paths.cast_output_root, paths.build_dir]);
|
||||
})
|
||||
);
|
||||
|
||||
gulp.task("clean-hassio", () =>
|
||||
del([paths.hassio_output_root, paths.build_dir])
|
||||
);
|
||||
gulp.task("clean-hassio", function cleanOutputAndBuildDir() {
|
||||
return del([paths.hassio_output_root, paths.build_dir]);
|
||||
});
|
||||
|
||||
gulp.task(
|
||||
"clean-gallery",
|
||||
gulp.parallel("clean-translations", () =>
|
||||
del([paths.gallery_output_root, paths.build_dir])
|
||||
)
|
||||
gulp.parallel("clean-translations", function cleanOutputAndBuildDir() {
|
||||
return del([paths.gallery_output_root, paths.build_dir]);
|
||||
})
|
||||
);
|
||||
|
@@ -302,23 +302,15 @@ gulp.task("gen-index-hassio-prod", async () => {
|
||||
|
||||
function writeHassioEntrypoint(latestEntrypoint, es5Entrypoint) {
|
||||
fs.mkdirSync(paths.hassio_output_root, { recursive: true });
|
||||
// Safari 12 and below does not have a compliant ES2015 implementation of template literals, so we ship ES5
|
||||
fs.writeFileSync(
|
||||
path.resolve(paths.hassio_output_root, "entrypoint.js"),
|
||||
`
|
||||
function loadES5() {
|
||||
try {
|
||||
new Function("import('${latestEntrypoint}')")();
|
||||
} catch (err) {
|
||||
var el = document.createElement('script');
|
||||
el.src = '${es5Entrypoint}';
|
||||
document.body.appendChild(el);
|
||||
}
|
||||
if (/.*Version\\/(?:11|12)(?:\\.\\d+)*.*Safari\\//.test(navigator.userAgent)) {
|
||||
loadES5();
|
||||
} else {
|
||||
try {
|
||||
new Function("import('${latestEntrypoint}')")();
|
||||
} catch (err) {
|
||||
loadES5();
|
||||
}
|
||||
}
|
||||
`,
|
||||
{ encoding: "utf-8" }
|
||||
|
@@ -2,6 +2,7 @@
|
||||
|
||||
const gulp = require("gulp");
|
||||
const path = require("path");
|
||||
const cpx = require("cpx");
|
||||
const fs = require("fs-extra");
|
||||
const paths = require("../paths");
|
||||
|
||||
@@ -12,10 +13,8 @@ const polyPath = (...parts) => path.resolve(paths.polymer_dir, ...parts);
|
||||
const copyFileDir = (fromFile, toDir) =>
|
||||
fs.copySync(fromFile, path.join(toDir, path.basename(fromFile)));
|
||||
|
||||
const genStaticPath =
|
||||
(staticDir) =>
|
||||
(...parts) =>
|
||||
path.resolve(staticDir, ...parts);
|
||||
const genStaticPath = (staticDir) => (...parts) =>
|
||||
path.resolve(staticDir, ...parts);
|
||||
|
||||
function copyTranslations(staticDir) {
|
||||
const staticPath = genStaticPath(staticDir);
|
||||
@@ -63,12 +62,9 @@ function copyLoaderJS(staticDir) {
|
||||
function copyFonts(staticDir) {
|
||||
const staticPath = genStaticPath(staticDir);
|
||||
// Local fonts
|
||||
fs.copySync(
|
||||
npmPath("roboto-fontface/fonts/roboto/"),
|
||||
staticPath("fonts/roboto/"),
|
||||
{
|
||||
filter: (src) => !src.includes(".") || src.endsWith(".woff2"),
|
||||
}
|
||||
cpx.copySync(
|
||||
npmPath("roboto-fontface/fonts/roboto/*.woff2"),
|
||||
staticPath("fonts/roboto")
|
||||
);
|
||||
}
|
||||
|
||||
|
@@ -1,5 +1,3 @@
|
||||
/* eslint-disable @typescript-eslint/no-var-requires */
|
||||
|
||||
const crypto = require("crypto");
|
||||
const del = require("del");
|
||||
const path = require("path");
|
||||
@@ -28,6 +26,13 @@ gulp.task("translations-enable-merge-backend", (done) => {
|
||||
done();
|
||||
});
|
||||
|
||||
String.prototype.rsplit = function (sep, maxsplit) {
|
||||
var split = this.split(sep);
|
||||
return maxsplit
|
||||
? [split.slice(0, -maxsplit).join(sep)].concat(split.slice(-maxsplit))
|
||||
: split;
|
||||
};
|
||||
|
||||
// Panel translations which should be split from the core translations.
|
||||
const TRANSLATION_FRAGMENTS = Object.keys(
|
||||
require("../../src/translations/en.json").ui.panel
|
||||
@@ -35,7 +40,7 @@ const TRANSLATION_FRAGMENTS = Object.keys(
|
||||
|
||||
function recursiveFlatten(prefix, data) {
|
||||
let output = {};
|
||||
Object.keys(data).forEach((key) => {
|
||||
Object.keys(data).forEach(function (key) {
|
||||
if (typeof data[key] === "object") {
|
||||
output = {
|
||||
...output,
|
||||
@@ -96,19 +101,15 @@ function lokaliseTransform(data, original, file) {
|
||||
if (value instanceof Object) {
|
||||
output[key] = lokaliseTransform(value, original, file);
|
||||
} else {
|
||||
output[key] = value.replace(re_key_reference, (_match, lokalise_key) => {
|
||||
const replace = lokalise_key.split("::").reduce((tr, k) => {
|
||||
output[key] = value.replace(re_key_reference, (match, key) => {
|
||||
const replace = key.split("::").reduce((tr, k) => {
|
||||
if (!tr) {
|
||||
throw Error(
|
||||
`Invalid key placeholder ${lokalise_key} in ${file.path}`
|
||||
);
|
||||
throw Error(`Invalid key placeholder ${key} in ${file.path}`);
|
||||
}
|
||||
return tr[k];
|
||||
}, original);
|
||||
if (typeof replace !== "string") {
|
||||
throw Error(
|
||||
`Invalid key placeholder ${lokalise_key} in ${file.path}`
|
||||
);
|
||||
throw Error(`Invalid key placeholder ${key} in ${file.path}`);
|
||||
}
|
||||
return replace;
|
||||
});
|
||||
@@ -117,7 +118,9 @@ function lokaliseTransform(data, original, file) {
|
||||
return output;
|
||||
}
|
||||
|
||||
gulp.task("clean-translations", () => del([workDir]));
|
||||
gulp.task("clean-translations", function () {
|
||||
return del([workDir]);
|
||||
});
|
||||
|
||||
gulp.task("ensure-translations-build-dir", (done) => {
|
||||
if (!fs.existsSync(workDir)) {
|
||||
@@ -126,7 +129,7 @@ gulp.task("ensure-translations-build-dir", (done) => {
|
||||
done();
|
||||
});
|
||||
|
||||
gulp.task("create-test-metadata", (cb) => {
|
||||
gulp.task("create-test-metadata", function (cb) {
|
||||
fs.writeFile(
|
||||
workDir + "/testMetadata.json",
|
||||
JSON.stringify({
|
||||
@@ -140,13 +143,17 @@ gulp.task("create-test-metadata", (cb) => {
|
||||
|
||||
gulp.task(
|
||||
"create-test-translation",
|
||||
gulp.series("create-test-metadata", () =>
|
||||
gulp
|
||||
gulp.series("create-test-metadata", function createTestTranslation() {
|
||||
return gulp
|
||||
.src(path.join(paths.translations_src, "en.json"))
|
||||
.pipe(transform((data, _file) => recursiveEmpty(data)))
|
||||
.pipe(
|
||||
transform(function (data, file) {
|
||||
return recursiveEmpty(data);
|
||||
})
|
||||
)
|
||||
.pipe(rename("test.json"))
|
||||
.pipe(gulp.dest(workDir))
|
||||
)
|
||||
.pipe(gulp.dest(workDir));
|
||||
})
|
||||
);
|
||||
|
||||
/**
|
||||
@@ -158,7 +165,7 @@ gulp.task(
|
||||
* project is buildable immediately after merging new translation keys, since
|
||||
* the Lokalise update to translations/en.json will not happen immediately.
|
||||
*/
|
||||
gulp.task("build-master-translation", () => {
|
||||
gulp.task("build-master-translation", function () {
|
||||
const src = [path.join(paths.translations_src, "en.json")];
|
||||
|
||||
if (mergeBackend) {
|
||||
@@ -167,7 +174,11 @@ gulp.task("build-master-translation", () => {
|
||||
|
||||
return gulp
|
||||
.src(src)
|
||||
.pipe(transform((data, file) => lokaliseTransform(data, data, file)))
|
||||
.pipe(
|
||||
transform(function (data, file) {
|
||||
return lokaliseTransform(data, data, file);
|
||||
})
|
||||
)
|
||||
.pipe(
|
||||
merge({
|
||||
fileName: "translationMaster.json",
|
||||
@@ -176,14 +187,18 @@ gulp.task("build-master-translation", () => {
|
||||
.pipe(gulp.dest(workDir));
|
||||
});
|
||||
|
||||
gulp.task("build-merged-translations", () =>
|
||||
gulp
|
||||
gulp.task("build-merged-translations", function () {
|
||||
return gulp
|
||||
.src([inFrontendDir + "/*.json", workDir + "/test.json"], {
|
||||
allowEmpty: true,
|
||||
})
|
||||
.pipe(transform((data, file) => lokaliseTransform(data, data, file)))
|
||||
.pipe(
|
||||
foreach((stream, file) => {
|
||||
transform(function (data, file) {
|
||||
return lokaliseTransform(data, data, file);
|
||||
})
|
||||
)
|
||||
.pipe(
|
||||
foreach(function (stream, file) {
|
||||
// For each language generate a merged json file. It begins with the master
|
||||
// translation as a failsafe for untranslated strings, and merges all parent
|
||||
// tags into one file for each specific subtag
|
||||
@@ -215,17 +230,17 @@ gulp.task("build-merged-translations", () =>
|
||||
)
|
||||
.pipe(gulp.dest(fullDir));
|
||||
})
|
||||
)
|
||||
);
|
||||
);
|
||||
});
|
||||
|
||||
let taskName;
|
||||
var taskName;
|
||||
|
||||
const splitTasks = [];
|
||||
TRANSLATION_FRAGMENTS.forEach((fragment) => {
|
||||
taskName = "build-translation-fragment-" + fragment;
|
||||
gulp.task(taskName, () =>
|
||||
gulp.task(taskName, function () {
|
||||
// Return only the translations for this fragment.
|
||||
gulp
|
||||
return gulp
|
||||
.src(fullDir + "/*.json")
|
||||
.pipe(
|
||||
transform((data) => ({
|
||||
@@ -236,18 +251,18 @@ TRANSLATION_FRAGMENTS.forEach((fragment) => {
|
||||
},
|
||||
}))
|
||||
)
|
||||
.pipe(gulp.dest(workDir + "/" + fragment))
|
||||
);
|
||||
.pipe(gulp.dest(workDir + "/" + fragment));
|
||||
});
|
||||
splitTasks.push(taskName);
|
||||
});
|
||||
|
||||
taskName = "build-translation-core";
|
||||
gulp.task(taskName, () =>
|
||||
gulp.task(taskName, function () {
|
||||
// Remove the fragment translations from the core translation.
|
||||
gulp
|
||||
return gulp
|
||||
.src(fullDir + "/*.json")
|
||||
.pipe(
|
||||
transform((data, _file) => {
|
||||
transform((data, file) => {
|
||||
TRANSLATION_FRAGMENTS.forEach((fragment) => {
|
||||
delete data.ui.panel[fragment];
|
||||
});
|
||||
@@ -255,14 +270,14 @@ gulp.task(taskName, () =>
|
||||
return data;
|
||||
})
|
||||
)
|
||||
.pipe(gulp.dest(coreDir))
|
||||
);
|
||||
.pipe(gulp.dest(coreDir));
|
||||
});
|
||||
|
||||
splitTasks.push(taskName);
|
||||
|
||||
gulp.task("build-flattened-translations", () =>
|
||||
gulp.task("build-flattened-translations", function () {
|
||||
// Flatten the split versions of our translations, and move them into outDir
|
||||
gulp
|
||||
return gulp
|
||||
.src(
|
||||
TRANSLATION_FRAGMENTS.map(
|
||||
(fragment) => workDir + "/" + fragment + "/*.json"
|
||||
@@ -270,45 +285,41 @@ gulp.task("build-flattened-translations", () =>
|
||||
{ base: workDir }
|
||||
)
|
||||
.pipe(
|
||||
transform((data) =>
|
||||
transform(function (data) {
|
||||
// Polymer.AppLocalizeBehavior requires flattened json
|
||||
flatten(data)
|
||||
)
|
||||
return flatten(data);
|
||||
})
|
||||
)
|
||||
.pipe(
|
||||
rename((filePath) => {
|
||||
if (filePath.dirname === "core") {
|
||||
filePath.dirname = "";
|
||||
}
|
||||
// In dev we create the file with the fake hash in the filename
|
||||
if (!env.isProdBuild()) {
|
||||
filePath.basename += "-dev";
|
||||
}
|
||||
})
|
||||
)
|
||||
.pipe(gulp.dest(outDir))
|
||||
);
|
||||
.pipe(gulp.dest(outDir));
|
||||
});
|
||||
|
||||
const fingerprints = {};
|
||||
|
||||
gulp.task("build-translation-fingerprints", () => {
|
||||
// Fingerprint full file of each language
|
||||
const files = fs.readdirSync(fullDir);
|
||||
gulp.task(
|
||||
"build-translation-fingerprints",
|
||||
function fingerprintTranslationFiles() {
|
||||
// Fingerprint full file of each language
|
||||
const files = fs.readdirSync(fullDir);
|
||||
|
||||
for (let i = 0; i < files.length; i++) {
|
||||
fingerprints[files[i].split(".")[0]] = {
|
||||
// In dev we create fake hashes
|
||||
hash: env.isProdBuild()
|
||||
? crypto
|
||||
.createHash("md5")
|
||||
.update(fs.readFileSync(path.join(fullDir, files[i]), "utf-8"))
|
||||
.digest("hex")
|
||||
: "dev",
|
||||
};
|
||||
}
|
||||
for (let i = 0; i < files.length; i++) {
|
||||
fingerprints[files[i].split(".")[0]] = {
|
||||
// In dev we create fake hashes
|
||||
hash: env.isProdBuild()
|
||||
? crypto
|
||||
.createHash("md5")
|
||||
.update(fs.readFileSync(path.join(fullDir, files[i]), "utf-8"))
|
||||
.digest("hex")
|
||||
: "dev",
|
||||
};
|
||||
}
|
||||
|
||||
// In dev we create the file with the fake hash in the filename
|
||||
if (env.isProdBuild()) {
|
||||
mapFiles(outDir, ".json", (filename) => {
|
||||
const parsed = path.parse(filename);
|
||||
|
||||
@@ -324,43 +335,35 @@ gulp.task("build-translation-fingerprints", () => {
|
||||
}`
|
||||
);
|
||||
});
|
||||
|
||||
const stream = source("translationFingerprints.json");
|
||||
stream.write(JSON.stringify(fingerprints));
|
||||
process.nextTick(() => stream.end());
|
||||
return stream.pipe(vinylBuffer()).pipe(gulp.dest(workDir));
|
||||
}
|
||||
);
|
||||
|
||||
const stream = source("translationFingerprints.json");
|
||||
stream.write(JSON.stringify(fingerprints));
|
||||
process.nextTick(() => stream.end());
|
||||
return stream.pipe(vinylBuffer()).pipe(gulp.dest(workDir));
|
||||
});
|
||||
|
||||
gulp.task("build-translation-fragment-supervisor", () =>
|
||||
gulp
|
||||
gulp.task("build-translation-fragment-supervisor", function () {
|
||||
return gulp
|
||||
.src(fullDir + "/*.json")
|
||||
.pipe(transform((data) => data.supervisor))
|
||||
.pipe(
|
||||
rename((filePath) => {
|
||||
// In dev we create the file with the fake hash in the filename
|
||||
if (!env.isProdBuild()) {
|
||||
filePath.basename += "-dev";
|
||||
}
|
||||
})
|
||||
)
|
||||
.pipe(gulp.dest(workDir + "/supervisor"))
|
||||
);
|
||||
.pipe(gulp.dest(workDir + "/supervisor"));
|
||||
});
|
||||
|
||||
gulp.task("build-translation-flatten-supervisor", () =>
|
||||
gulp
|
||||
gulp.task("build-translation-flatten-supervisor", function () {
|
||||
return gulp
|
||||
.src(workDir + "/supervisor/*.json")
|
||||
.pipe(
|
||||
transform((data) =>
|
||||
transform(function (data) {
|
||||
// Polymer.AppLocalizeBehavior requires flattened json
|
||||
flatten(data)
|
||||
)
|
||||
return flatten(data);
|
||||
})
|
||||
)
|
||||
.pipe(gulp.dest(outDir))
|
||||
);
|
||||
.pipe(gulp.dest(outDir));
|
||||
});
|
||||
|
||||
gulp.task("build-translation-write-metadata", () =>
|
||||
gulp
|
||||
gulp.task("build-translation-write-metadata", function writeMetadata() {
|
||||
return gulp
|
||||
.src(
|
||||
[
|
||||
path.join(paths.translations_src, "translationMetadata.json"),
|
||||
@@ -371,14 +374,13 @@ gulp.task("build-translation-write-metadata", () =>
|
||||
)
|
||||
.pipe(merge({}))
|
||||
.pipe(
|
||||
transform((data) => {
|
||||
transform(function (data) {
|
||||
const newData = {};
|
||||
Object.entries(data).forEach(([key, value]) => {
|
||||
// Filter out translations without native name.
|
||||
if (value.nativeName) {
|
||||
newData[key] = value;
|
||||
} else {
|
||||
// eslint-disable-next-line no-console
|
||||
console.warn(
|
||||
`Skipping language ${key}. Native name was not translated.`
|
||||
);
|
||||
@@ -394,26 +396,19 @@ gulp.task("build-translation-write-metadata", () =>
|
||||
}))
|
||||
)
|
||||
.pipe(rename("translationMetadata.json"))
|
||||
.pipe(gulp.dest(workDir))
|
||||
);
|
||||
|
||||
gulp.task(
|
||||
"create-translations",
|
||||
gulp.series(
|
||||
env.isProdBuild() ? (done) => done() : "create-test-translation",
|
||||
"build-master-translation",
|
||||
"build-merged-translations",
|
||||
gulp.parallel(...splitTasks),
|
||||
"build-flattened-translations"
|
||||
)
|
||||
);
|
||||
.pipe(gulp.dest(workDir));
|
||||
});
|
||||
|
||||
gulp.task(
|
||||
"build-translations",
|
||||
gulp.series(
|
||||
"clean-translations",
|
||||
"ensure-translations-build-dir",
|
||||
"create-translations",
|
||||
env.isProdBuild() ? (done) => done() : "create-test-translation",
|
||||
"build-master-translation",
|
||||
"build-merged-translations",
|
||||
gulp.parallel(...splitTasks),
|
||||
"build-flattened-translations",
|
||||
"build-translation-fingerprints",
|
||||
"build-translation-write-metadata"
|
||||
)
|
||||
|
@@ -1,6 +1,4 @@
|
||||
/* eslint-disable @typescript-eslint/no-var-requires */
|
||||
// Tasks to run webpack.
|
||||
const fs = require("fs");
|
||||
const gulp = require("gulp");
|
||||
const webpack = require("webpack");
|
||||
const WebpackDevServer = require("webpack-dev-server");
|
||||
@@ -20,13 +18,6 @@ const bothBuilds = (createConfigFunc, params) => [
|
||||
createConfigFunc({ ...params, latestBuild: false }),
|
||||
];
|
||||
|
||||
const isWsl =
|
||||
fs.existsSync("/proc/version") &&
|
||||
fs
|
||||
.readFileSync("/proc/version", "utf-8")
|
||||
.toLocaleLowerCase()
|
||||
.includes("microsoft");
|
||||
|
||||
/**
|
||||
* @param {{
|
||||
* compiler: import("webpack").Compiler,
|
||||
@@ -45,7 +36,7 @@ const runDevServer = ({
|
||||
open: true,
|
||||
watchContentBase: true,
|
||||
contentBase,
|
||||
}).listen(port, listenHost, (err) => {
|
||||
}).listen(port, listenHost, function (err) {
|
||||
if (err) {
|
||||
throw err;
|
||||
}
|
||||
@@ -66,7 +57,6 @@ const doneHandler = (done) => (err, stats) => {
|
||||
}
|
||||
|
||||
if (stats.hasErrors() || stats.hasWarnings()) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(stats.toString("minimal"));
|
||||
}
|
||||
|
||||
@@ -88,14 +78,13 @@ const prodBuild = (conf) =>
|
||||
|
||||
gulp.task("webpack-watch-app", () => {
|
||||
// This command will run forever because we don't close compiler
|
||||
webpack(
|
||||
process.env.ES5
|
||||
? bothBuilds(createAppConfig, { isProdBuild: false })
|
||||
: createAppConfig({ isProdBuild: false, latestBuild: true })
|
||||
).watch({ poll: isWsl }, doneHandler());
|
||||
webpack(createAppConfig({ isProdBuild: false, latestBuild: true })).watch(
|
||||
{ ignored: /build-translations/ },
|
||||
doneHandler()
|
||||
);
|
||||
gulp.watch(
|
||||
path.join(paths.translations_src, "en.json"),
|
||||
gulp.series("create-translations", "copy-translations-app")
|
||||
gulp.series("build-translations", "copy-translations-app")
|
||||
);
|
||||
});
|
||||
|
||||
@@ -148,7 +137,7 @@ gulp.task("webpack-watch-hassio", () => {
|
||||
isProdBuild: false,
|
||||
latestBuild: true,
|
||||
})
|
||||
).watch({ ignored: /build-translations/, poll: isWsl }, doneHandler());
|
||||
).watch({ ignored: /build-translations/ }, doneHandler());
|
||||
|
||||
gulp.watch(
|
||||
path.join(paths.translations_src, "en.json"),
|
||||
|
File diff suppressed because one or more lines are too long
@@ -57,6 +57,7 @@ const createRollupConfig = ({
|
||||
babel({
|
||||
...bundle.babelOptions({ latestBuild }),
|
||||
extensions,
|
||||
exclude: bundle.babelExclude(),
|
||||
babelHelpers: isWDS ? "inline" : "bundled",
|
||||
}),
|
||||
string({
|
||||
|
@@ -6,7 +6,6 @@ const { WebpackManifestPlugin } = require("webpack-manifest-plugin");
|
||||
const paths = require("./paths.js");
|
||||
const bundle = require("./bundle.js");
|
||||
const log = require("fancy-log");
|
||||
const WebpackBar = require("webpackbar");
|
||||
|
||||
class LogStartCompilePlugin {
|
||||
ignoredFirst = false;
|
||||
@@ -48,18 +47,15 @@ const createWebpackConfig = ({
|
||||
rules: [
|
||||
{
|
||||
test: /\.m?js$|\.ts$/,
|
||||
exclude: bundle.babelExclude(),
|
||||
use: {
|
||||
loader: "babel-loader",
|
||||
options: {
|
||||
...bundle.babelOptions({ latestBuild }),
|
||||
cacheDirectory: !isProdBuild,
|
||||
cacheCompression: false,
|
||||
},
|
||||
options: bundle.babelOptions({ latestBuild }),
|
||||
},
|
||||
},
|
||||
{
|
||||
test: /\.css$/,
|
||||
type: "asset/source",
|
||||
use: "raw-loader",
|
||||
},
|
||||
],
|
||||
},
|
||||
@@ -71,11 +67,8 @@ const createWebpackConfig = ({
|
||||
terserOptions: bundle.terserOptions(latestBuild),
|
||||
}),
|
||||
],
|
||||
moduleIds: isProdBuild && !isStatsBuild ? "deterministic" : "named",
|
||||
chunkIds: isProdBuild && !isStatsBuild ? "deterministic" : "named",
|
||||
},
|
||||
plugins: [
|
||||
new WebpackBar({ fancy: !isProdBuild }),
|
||||
new WebpackManifestPlugin({
|
||||
// Only include the JS of entrypoints
|
||||
filter: (file) => file.isInitial && !file.name.endsWith(".map"),
|
||||
@@ -120,6 +113,15 @@ const createWebpackConfig = ({
|
||||
new RegExp(bundle.emptyPackages({ latestBuild }).join("|")),
|
||||
path.resolve(paths.polymer_dir, "src/util/empty.js")
|
||||
),
|
||||
// We need to change the import of the polyfill for EventTarget, so we replace the polyfill file with our customized one
|
||||
new webpack.NormalModuleReplacementPlugin(
|
||||
new RegExp(
|
||||
require.resolve(
|
||||
"@lit-labs/virtualizer/lib/uni-virtualizer/lib/polyfillLoaders/EventTarget.js"
|
||||
)
|
||||
),
|
||||
path.resolve(paths.polymer_dir, "src/resources/EventTarget-ponyfill.js")
|
||||
),
|
||||
!isProdBuild && new LogStartCompilePlugin(),
|
||||
].filter(Boolean),
|
||||
resolve: {
|
||||
@@ -127,33 +129,25 @@ const createWebpackConfig = ({
|
||||
alias: {
|
||||
"lit/decorators$": "lit/decorators.js",
|
||||
"lit/directive$": "lit/directive.js",
|
||||
"lit/directives/until$": "lit/directives/until.js",
|
||||
"lit/directives/class-map$": "lit/directives/class-map.js",
|
||||
"lit/directives/style-map$": "lit/directives/style-map.js",
|
||||
"lit/directives/if-defined$": "lit/directives/if-defined.js",
|
||||
"lit/directives/guard$": "lit/directives/guard.js",
|
||||
"lit/directives/cache$": "lit/directives/cache.js",
|
||||
"lit/directives/repeat$": "lit/directives/repeat.js",
|
||||
"lit/polyfill-support$": "lit/polyfill-support.js",
|
||||
},
|
||||
},
|
||||
output: {
|
||||
filename: ({ chunk }) => {
|
||||
if (!isProdBuild || isStatsBuild || dontHash.has(chunk.name)) {
|
||||
if (!isProdBuild || dontHash.has(chunk.name)) {
|
||||
return `${chunk.name}.js`;
|
||||
}
|
||||
return `${chunk.name}.${chunk.hash.substr(0, 8)}.js`;
|
||||
},
|
||||
chunkFilename:
|
||||
isProdBuild && !isStatsBuild ? "[chunkhash:8].js" : "[id].chunk.js",
|
||||
isProdBuild && !isStatsBuild
|
||||
? "chunk.[chunkhash].js"
|
||||
: "[name].chunk.js",
|
||||
path: outputPath,
|
||||
publicPath,
|
||||
// To silence warning in worker plugin
|
||||
globalObject: "self",
|
||||
},
|
||||
experiments: {
|
||||
topLevelAwait: true,
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
|
@@ -139,7 +139,7 @@
|
||||
Your authentication credentials or Home Assistant url are never sent
|
||||
to the Cloud. You can validate this behavior in
|
||||
<a
|
||||
href="https://github.com/home-assistant/frontend/tree/dev/cast"
|
||||
href="https://github.com/home-assistant/home-assistant-polymer/tree/dev/cast"
|
||||
target="_blank"
|
||||
>the source code</a
|
||||
>.
|
||||
|
@@ -191,7 +191,7 @@ class HcCast extends LitElement {
|
||||
}
|
||||
this.connection.close();
|
||||
location.reload();
|
||||
} catch (err: any) {
|
||||
} catch (err) {
|
||||
alert("Unable to log out!");
|
||||
}
|
||||
}
|
||||
|
@@ -212,7 +212,7 @@ export class HcConnect extends LitElement {
|
||||
let url: URL;
|
||||
try {
|
||||
url = new URL(value);
|
||||
} catch (err: any) {
|
||||
} catch (err) {
|
||||
this.error = "Invalid URL";
|
||||
return;
|
||||
}
|
||||
@@ -240,7 +240,7 @@ export class HcConnect extends LitElement {
|
||||
try {
|
||||
this.loading = true;
|
||||
auth = await getAuth(options);
|
||||
} catch (err: any) {
|
||||
} catch (err) {
|
||||
if (init === "saved-tokens" && err === ERR_CANNOT_CONNECT) {
|
||||
this.cannotConnect = true;
|
||||
return;
|
||||
@@ -259,7 +259,7 @@ export class HcConnect extends LitElement {
|
||||
|
||||
try {
|
||||
conn = await createConnection({ auth });
|
||||
} catch (err: any) {
|
||||
} catch (err) {
|
||||
// In case of saved tokens, silently solve problems.
|
||||
if (init === "saved-tokens") {
|
||||
if (err === ERR_CANNOT_CONNECT) {
|
||||
@@ -285,7 +285,7 @@ export class HcConnect extends LitElement {
|
||||
try {
|
||||
saveTokens(null);
|
||||
location.reload();
|
||||
} catch (err: any) {
|
||||
} catch (err) {
|
||||
alert("Unable to log out!");
|
||||
}
|
||||
}
|
||||
|
@@ -5,8 +5,8 @@ import {
|
||||
import { castContext } from "../cast_context";
|
||||
|
||||
export const castDemoLovelace: () => LovelaceConfig = () => {
|
||||
const touchSupported =
|
||||
castContext.getDeviceCapabilities().touch_input_supported;
|
||||
const touchSupported = castContext.getDeviceCapabilities()
|
||||
.touch_input_supported;
|
||||
return {
|
||||
views: [
|
||||
{
|
||||
|
@@ -148,14 +148,14 @@ export class HcMain extends HassElement {
|
||||
expires_in: 0,
|
||||
}),
|
||||
});
|
||||
} catch (err: any) {
|
||||
} catch (err) {
|
||||
this._error = this._getErrorMessage(err);
|
||||
return;
|
||||
}
|
||||
let connection;
|
||||
try {
|
||||
connection = await createConnection({ auth });
|
||||
} catch (err: any) {
|
||||
} catch (err) {
|
||||
this._error = this._getErrorMessage(err);
|
||||
return;
|
||||
}
|
||||
@@ -193,7 +193,7 @@ export class HcMain extends HassElement {
|
||||
this._unsubLovelace = llColl.subscribe((lovelaceConfig) =>
|
||||
this._handleNewLovelaceConfig(lovelaceConfig)
|
||||
);
|
||||
} catch (err: any) {
|
||||
} catch (err) {
|
||||
// eslint-disable-next-line
|
||||
console.log("Error fetching Lovelace configuration", err, msg);
|
||||
// Generate a Lovelace config.
|
||||
|
@@ -29,11 +29,6 @@ export const demoLovelaceArsaboo: DemoConfig["lovelace"] = (localize) => ({
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: "Energy distribution today",
|
||||
type: "energy-distribution",
|
||||
link_dashboard: true,
|
||||
},
|
||||
{
|
||||
type: "thermostat",
|
||||
entity: "climate.upstairs",
|
||||
@@ -118,7 +113,8 @@ export const demoLovelaceArsaboo: DemoConfig["lovelace"] = (localize) => ({
|
||||
on: "/assets/arsaboo/icons/light_bulb_on.png",
|
||||
},
|
||||
state_filter: {
|
||||
on: "brightness(130%) saturate(1.5) drop-shadow(0px 0px 10px gold)",
|
||||
on:
|
||||
"brightness(130%) saturate(1.5) drop-shadow(0px 0px 10px gold)",
|
||||
off: "brightness(80%) saturate(0.8)",
|
||||
},
|
||||
style: {
|
||||
@@ -200,7 +196,8 @@ export const demoLovelaceArsaboo: DemoConfig["lovelace"] = (localize) => ({
|
||||
on: "/assets/arsaboo/icons/light_bulb_on.png",
|
||||
},
|
||||
state_filter: {
|
||||
on: "brightness(130%) saturate(1.5) drop-shadow(0px 0px 10px gold)",
|
||||
on:
|
||||
"brightness(130%) saturate(1.5) drop-shadow(0px 0px 10px gold)",
|
||||
off: "brightness(80%) saturate(0.8)",
|
||||
},
|
||||
style: {
|
||||
@@ -280,7 +277,8 @@ export const demoLovelaceArsaboo: DemoConfig["lovelace"] = (localize) => ({
|
||||
on: "/assets/arsaboo/icons/light_bulb_on.png",
|
||||
},
|
||||
state_filter: {
|
||||
on: "brightness(130%) saturate(1.5) drop-shadow(0px 0px 10px gold)",
|
||||
on:
|
||||
"brightness(130%) saturate(1.5) drop-shadow(0px 0px 10px gold)",
|
||||
off: "brightness(80%) saturate(0.8)",
|
||||
},
|
||||
style: {
|
||||
@@ -317,7 +315,8 @@ export const demoLovelaceArsaboo: DemoConfig["lovelace"] = (localize) => ({
|
||||
on: "/assets/arsaboo/icons/light_bulb_on.png",
|
||||
},
|
||||
state_filter: {
|
||||
on: "brightness(130%) saturate(1.5) drop-shadow(0px 0px 10px gold)",
|
||||
on:
|
||||
"brightness(130%) saturate(1.5) drop-shadow(0px 0px 10px gold)",
|
||||
off: "brightness(80%) saturate(0.8)",
|
||||
},
|
||||
style: {
|
||||
|
@@ -1,6 +1,5 @@
|
||||
import { MockHomeAssistant } from "../../../src/fake_data/provide_hass";
|
||||
import { Lovelace } from "../../../src/panels/lovelace/types";
|
||||
import { energyEntities } from "../stubs/entities";
|
||||
import { DemoConfig } from "./types";
|
||||
|
||||
export const demoConfigs: Array<() => Promise<DemoConfig>> = [
|
||||
@@ -13,8 +12,9 @@ export const demoConfigs: Array<() => Promise<DemoConfig>> = [
|
||||
// eslint-disable-next-line import/no-mutable-exports
|
||||
export let selectedDemoConfigIndex = 0;
|
||||
// eslint-disable-next-line import/no-mutable-exports
|
||||
export let selectedDemoConfig: Promise<DemoConfig> =
|
||||
demoConfigs[selectedDemoConfigIndex]();
|
||||
export let selectedDemoConfig: Promise<DemoConfig> = demoConfigs[
|
||||
selectedDemoConfigIndex
|
||||
]();
|
||||
|
||||
export const setDemoConfig = async (
|
||||
hass: MockHomeAssistant,
|
||||
@@ -28,7 +28,6 @@ export const setDemoConfig = async (
|
||||
selectedDemoConfig = confProm;
|
||||
|
||||
hass.addEntities(config.entities(hass.localize), true);
|
||||
hass.addEntities(energyEntities());
|
||||
lovelace.saveConfig(config.lovelace(hass.localize));
|
||||
hass.mockTheme(config.theme());
|
||||
};
|
||||
|
@@ -980,7 +980,8 @@ export const demoEntitiesTeachingbirds: DemoConfig["entities"] = () =>
|
||||
icon: "mdi:account-off",
|
||||
custom_ui_state_card: "state-card-custom-ui",
|
||||
templates: {
|
||||
icon: "if (state === 'on') return 'mdi:account'; else if (state === 'off') return 'mdi:account-off';\n",
|
||||
icon:
|
||||
"if (state === 'on') return 'mdi:account'; else if (state === 'off') return 'mdi:account-off';\n",
|
||||
icon_color:
|
||||
"if (state === 'on') return 'rgb(56, 150, 56)'; else if (state === 'off') return 'rgb(249, 251, 255)';\n",
|
||||
},
|
||||
@@ -1004,7 +1005,8 @@ export const demoEntitiesTeachingbirds: DemoConfig["entities"] = () =>
|
||||
icon: "mdi:account-multiple-minus",
|
||||
custom_ui_state_card: "state-card-custom-ui",
|
||||
templates: {
|
||||
icon: "if (state === 'on') return 'mdi:account-group'; else if (state === 'off') return 'mdi:account-multiple-minus';\n",
|
||||
icon:
|
||||
"if (state === 'on') return 'mdi:account-group'; else if (state === 'off') return 'mdi:account-multiple-minus';\n",
|
||||
icon_color:
|
||||
"if (state === 'on') return 'rgb(56, 150, 56)'; else if (state === 'off') return 'rgb(249, 251, 255)';\n",
|
||||
},
|
||||
|
@@ -19,7 +19,7 @@ export class HADemoCard extends LitElement implements LovelaceCard {
|
||||
|
||||
@property({ attribute: false }) public hass!: MockHomeAssistant;
|
||||
|
||||
@state() private _switching = false;
|
||||
@state() private _switching?: boolean;
|
||||
|
||||
private _hidden = localStorage.hide_demo_card;
|
||||
|
||||
@@ -27,7 +27,12 @@ export class HADemoCard extends LitElement implements LovelaceCard {
|
||||
return this._hidden ? 0 : 2;
|
||||
}
|
||||
|
||||
public setConfig(_config: LovelaceCardConfig) {}
|
||||
public setConfig(
|
||||
// @ts-ignore
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
config: LovelaceCardConfig
|
||||
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
||||
) {}
|
||||
|
||||
protected render(): TemplateResult {
|
||||
if (this._hidden) {
|
||||
@@ -44,7 +49,7 @@ export class HADemoCard extends LitElement implements LovelaceCard {
|
||||
(conf) => html`
|
||||
${conf.name}
|
||||
<small>
|
||||
<a target="_blank" href=${conf.authorUrl}>
|
||||
<a target="_blank" href="${conf.authorUrl}">
|
||||
${this.hass.localize(
|
||||
"ui.panel.page-demo.cards.demo.demo_by",
|
||||
"name",
|
||||
@@ -94,7 +99,7 @@ export class HADemoCard extends LitElement implements LovelaceCard {
|
||||
this._switching = true;
|
||||
try {
|
||||
await setDemoConfig(this.hass, this.lovelace!, index);
|
||||
} catch (err: any) {
|
||||
} catch (err) {
|
||||
alert("Failed to switch config :-(");
|
||||
} finally {
|
||||
this._switching = false;
|
||||
|
@@ -1,3 +1,5 @@
|
||||
import "@polymer/polymer/lib/elements/dom-if";
|
||||
import "@polymer/polymer/lib/elements/dom-repeat";
|
||||
import "../../src/resources/ha-style";
|
||||
import "../../src/resources/roboto";
|
||||
import "../../src/resources/safari-14-attachshadow-patch";
|
||||
|
@@ -20,9 +20,6 @@ import { mockShoppingList } from "./stubs/shopping_list";
|
||||
import { mockSystemLog } from "./stubs/system_log";
|
||||
import { mockTemplate } from "./stubs/template";
|
||||
import { mockTranslations } from "./stubs/translations";
|
||||
import { mockEnergy } from "./stubs/energy";
|
||||
import { mockConfig } from "./stubs/config";
|
||||
import { energyEntities } from "./stubs/entities";
|
||||
|
||||
class HaDemo extends HomeAssistantAppEl {
|
||||
protected async _initializeHass() {
|
||||
@@ -50,12 +47,8 @@ class HaDemo extends HomeAssistantAppEl {
|
||||
mockEvents(hass);
|
||||
mockMediaPlayer(hass);
|
||||
mockFrontend(hass);
|
||||
mockEnergy(hass);
|
||||
mockConfig(hass);
|
||||
mockPersistentNotification(hass);
|
||||
|
||||
hass.addEntities(energyEntities());
|
||||
|
||||
// Once config is loaded AND localize, set entities and apply theme.
|
||||
Promise.all([selectedDemoConfig, localizePromise]).then(
|
||||
([conf, localize]) => {
|
||||
|
@@ -1,41 +0,0 @@
|
||||
import { MockHomeAssistant } from "../../../src/fake_data/provide_hass";
|
||||
|
||||
export const mockConfig = (hass: MockHomeAssistant) => {
|
||||
hass.mockAPI("config/config_entries/entry", () => [
|
||||
{
|
||||
entry_id: "co2signal",
|
||||
domain: "co2signal",
|
||||
title: "CO2 Signal",
|
||||
source: "user",
|
||||
state: "loaded",
|
||||
supports_options: false,
|
||||
supports_unload: true,
|
||||
pref_disable_new_entities: false,
|
||||
pref_disable_polling: false,
|
||||
disabled_by: null,
|
||||
reason: null,
|
||||
},
|
||||
]);
|
||||
hass.mockWS("config/entity_registry/list", () => [
|
||||
{
|
||||
config_entry_id: "co2signal",
|
||||
device_id: "co2signal",
|
||||
area_id: null,
|
||||
disabled_by: null,
|
||||
entity_id: "sensor.co2_intensity",
|
||||
name: null,
|
||||
icon: null,
|
||||
platform: "co2signal",
|
||||
},
|
||||
{
|
||||
config_entry_id: "co2signal",
|
||||
device_id: "co2signal",
|
||||
area_id: null,
|
||||
disabled_by: null,
|
||||
entity_id: "sensor.grid_fossil_fuel_percentage",
|
||||
name: null,
|
||||
icon: null,
|
||||
platform: "co2signal",
|
||||
},
|
||||
]);
|
||||
};
|
@@ -1,134 +0,0 @@
|
||||
import { format, startOfToday, startOfTomorrow } from "date-fns";
|
||||
import { EnergySolarForecasts } from "../../../src/data/energy";
|
||||
import { MockHomeAssistant } from "../../../src/fake_data/provide_hass";
|
||||
|
||||
export const mockEnergy = (hass: MockHomeAssistant) => {
|
||||
hass.mockWS("energy/get_prefs", () => ({
|
||||
energy_sources: [
|
||||
{
|
||||
type: "grid",
|
||||
flow_from: [
|
||||
{
|
||||
stat_energy_from: "sensor.energy_consumption_tarif_1",
|
||||
stat_cost: "sensor.energy_consumption_tarif_1_cost",
|
||||
entity_energy_from: "sensor.energy_consumption_tarif_1",
|
||||
entity_energy_price: null,
|
||||
number_energy_price: null,
|
||||
},
|
||||
{
|
||||
stat_energy_from: "sensor.energy_consumption_tarif_2",
|
||||
stat_cost: "sensor.energy_consumption_tarif_2_cost",
|
||||
entity_energy_from: "sensor.energy_consumption_tarif_2",
|
||||
entity_energy_price: null,
|
||||
number_energy_price: null,
|
||||
},
|
||||
],
|
||||
flow_to: [
|
||||
{
|
||||
stat_energy_to: "sensor.energy_production_tarif_1",
|
||||
stat_compensation: "sensor.energy_production_tarif_1_compensation",
|
||||
entity_energy_to: "sensor.energy_production_tarif_1",
|
||||
entity_energy_price: null,
|
||||
number_energy_price: null,
|
||||
},
|
||||
{
|
||||
stat_energy_to: "sensor.energy_production_tarif_2",
|
||||
stat_compensation: "sensor.energy_production_tarif_2_compensation",
|
||||
entity_energy_to: "sensor.energy_production_tarif_2",
|
||||
entity_energy_price: null,
|
||||
number_energy_price: null,
|
||||
},
|
||||
],
|
||||
cost_adjustment_day: 0,
|
||||
},
|
||||
{
|
||||
type: "solar",
|
||||
stat_energy_from: "sensor.solar_production",
|
||||
config_entry_solar_forecast: ["solar_forecast"],
|
||||
},
|
||||
/* {
|
||||
type: "battery",
|
||||
stat_energy_from: "sensor.battery_output",
|
||||
stat_energy_to: "sensor.battery_input",
|
||||
}, */
|
||||
{
|
||||
type: "gas",
|
||||
stat_energy_from: "sensor.energy_gas",
|
||||
stat_cost: "sensor.energy_gas_cost",
|
||||
entity_energy_from: "sensor.energy_gas",
|
||||
entity_energy_price: null,
|
||||
number_energy_price: null,
|
||||
},
|
||||
],
|
||||
device_consumption: [
|
||||
{
|
||||
stat_consumption: "sensor.energy_car",
|
||||
},
|
||||
{
|
||||
stat_consumption: "sensor.energy_ac",
|
||||
},
|
||||
{
|
||||
stat_consumption: "sensor.energy_washing_machine",
|
||||
},
|
||||
{
|
||||
stat_consumption: "sensor.energy_dryer",
|
||||
},
|
||||
{
|
||||
stat_consumption: "sensor.energy_heat_pump",
|
||||
},
|
||||
{
|
||||
stat_consumption: "sensor.energy_boiler",
|
||||
},
|
||||
],
|
||||
}));
|
||||
hass.mockWS("energy/info", () => ({ cost_sensors: [] }));
|
||||
const todayString = format(startOfToday(), "yyyy-MM-dd");
|
||||
const tomorrowString = format(startOfTomorrow(), "yyyy-MM-dd");
|
||||
hass.mockWS(
|
||||
"energy/solar_forecast",
|
||||
(): EnergySolarForecasts => ({
|
||||
solar_forecast: {
|
||||
wh_hours: {
|
||||
[`${todayString}T06:00:00`]: 0,
|
||||
[`${todayString}T06:23:00`]: 6,
|
||||
[`${todayString}T06:45:00`]: 39,
|
||||
[`${todayString}T07:00:00`]: 28,
|
||||
[`${todayString}T08:00:00`]: 208,
|
||||
[`${todayString}T09:00:00`]: 352,
|
||||
[`${todayString}T10:00:00`]: 544,
|
||||
[`${todayString}T11:00:00`]: 748,
|
||||
[`${todayString}T12:00:00`]: 1259,
|
||||
[`${todayString}T13:00:00`]: 1361,
|
||||
[`${todayString}T14:00:00`]: 1373,
|
||||
[`${todayString}T15:00:00`]: 1370,
|
||||
[`${todayString}T16:00:00`]: 1186,
|
||||
[`${todayString}T17:00:00`]: 937,
|
||||
[`${todayString}T18:00:00`]: 652,
|
||||
[`${todayString}T19:00:00`]: 370,
|
||||
[`${todayString}T20:00:00`]: 155,
|
||||
[`${todayString}T21:48:00`]: 24,
|
||||
[`${todayString}T22:36:00`]: 0,
|
||||
[`${tomorrowString}T06:01:00`]: 0,
|
||||
[`${tomorrowString}T06:23:00`]: 9,
|
||||
[`${tomorrowString}T06:45:00`]: 47,
|
||||
[`${tomorrowString}T07:00:00`]: 48,
|
||||
[`${tomorrowString}T08:00:00`]: 473,
|
||||
[`${tomorrowString}T09:00:00`]: 827,
|
||||
[`${tomorrowString}T10:00:00`]: 1153,
|
||||
[`${tomorrowString}T11:00:00`]: 1413,
|
||||
[`${tomorrowString}T12:00:00`]: 1590,
|
||||
[`${tomorrowString}T13:00:00`]: 1652,
|
||||
[`${tomorrowString}T14:00:00`]: 1612,
|
||||
[`${tomorrowString}T15:00:00`]: 1438,
|
||||
[`${tomorrowString}T16:00:00`]: 1149,
|
||||
[`${tomorrowString}T17:00:00`]: 830,
|
||||
[`${tomorrowString}T18:00:00`]: 542,
|
||||
[`${tomorrowString}T19:00:00`]: 311,
|
||||
[`${tomorrowString}T20:00:00`]: 140,
|
||||
[`${tomorrowString}T21:47:00`]: 22,
|
||||
[`${tomorrowString}T22:34:00`]: 0,
|
||||
},
|
||||
},
|
||||
})
|
||||
);
|
||||
};
|
@@ -1,178 +0,0 @@
|
||||
import { convertEntities } from "../../../src/fake_data/entity";
|
||||
|
||||
export const energyEntities = () =>
|
||||
convertEntities({
|
||||
"sensor.grid_fossil_fuel_percentage": {
|
||||
entity_id: "sensor.grid_fossil_fuel_percentage",
|
||||
state: "88.6",
|
||||
attributes: {
|
||||
unit_of_measurement: "%",
|
||||
},
|
||||
},
|
||||
"sensor.solar_production": {
|
||||
entity_id: "sensor.solar_production",
|
||||
state: "88.6",
|
||||
attributes: {
|
||||
last_reset: "1970-01-01T00:00:00:00+00",
|
||||
friendly_name: "Solar",
|
||||
unit_of_measurement: "kWh",
|
||||
},
|
||||
},
|
||||
"sensor.battery_input": {
|
||||
entity_id: "sensor.battery_input",
|
||||
state: "4",
|
||||
attributes: {
|
||||
last_reset: "1970-01-01T00:00:00:00+00",
|
||||
friendly_name: "Battery Input",
|
||||
unit_of_measurement: "kWh",
|
||||
},
|
||||
},
|
||||
"sensor.battery_output": {
|
||||
entity_id: "sensor.battery_output",
|
||||
state: "3",
|
||||
attributes: {
|
||||
last_reset: "1970-01-01T00:00:00:00+00",
|
||||
friendly_name: "Battery Output",
|
||||
unit_of_measurement: "kWh",
|
||||
},
|
||||
},
|
||||
"sensor.energy_consumption_tarif_1": {
|
||||
entity_id: "sensor.energy_consumption_tarif_1 ",
|
||||
state: "88.6",
|
||||
attributes: {
|
||||
last_reset: "1970-01-01T00:00:00:00+00",
|
||||
friendly_name: "Grid consumption low tariff",
|
||||
unit_of_measurement: "kWh",
|
||||
},
|
||||
},
|
||||
"sensor.energy_consumption_tarif_2": {
|
||||
entity_id: "sensor.energy_consumption_tarif_2",
|
||||
state: "88.6",
|
||||
attributes: {
|
||||
last_reset: "1970-01-01T00:00:00:00+00",
|
||||
friendly_name: "Grid consumption high tariff",
|
||||
unit_of_measurement: "kWh",
|
||||
},
|
||||
},
|
||||
"sensor.energy_production_tarif_1": {
|
||||
entity_id: "sensor.energy_production_tarif_1",
|
||||
state: "88.6",
|
||||
attributes: {
|
||||
last_reset: "1970-01-01T00:00:00:00+00",
|
||||
friendly_name: "Returned to grid low tariff",
|
||||
unit_of_measurement: "kWh",
|
||||
},
|
||||
},
|
||||
"sensor.energy_production_tarif_2": {
|
||||
entity_id: "sensor.energy_production_tarif_2",
|
||||
state: "88.6",
|
||||
attributes: {
|
||||
last_reset: "1970-01-01T00:00:00:00+00",
|
||||
friendly_name: "Returned to grid high tariff",
|
||||
unit_of_measurement: "kWh",
|
||||
},
|
||||
},
|
||||
"sensor.energy_consumption_tarif_1_cost": {
|
||||
entity_id: "sensor.energy_consumption_tarif_1_cost",
|
||||
state: "2",
|
||||
attributes: {
|
||||
last_reset: "1970-01-01T00:00:00:00+00",
|
||||
unit_of_measurement: "EUR",
|
||||
},
|
||||
},
|
||||
"sensor.energy_consumption_tarif_2_cost": {
|
||||
entity_id: "sensor.energy_consumption_tarif_2_cost",
|
||||
state: "2",
|
||||
attributes: {
|
||||
last_reset: "1970-01-01T00:00:00:00+00",
|
||||
unit_of_measurement: "EUR",
|
||||
},
|
||||
},
|
||||
"sensor.energy_production_tarif_1_compensation": {
|
||||
entity_id: "sensor.energy_production_tarif_1_compensation",
|
||||
state: "2",
|
||||
attributes: {
|
||||
last_reset: "1970-01-01T00:00:00:00+00",
|
||||
unit_of_measurement: "EUR",
|
||||
},
|
||||
},
|
||||
"sensor.energy_production_tarif_2_compensation": {
|
||||
entity_id: "sensor.energy_production_tarif_2_compensation",
|
||||
state: "2",
|
||||
attributes: {
|
||||
last_reset: "1970-01-01T00:00:00:00+00",
|
||||
unit_of_measurement: "EUR",
|
||||
},
|
||||
},
|
||||
"sensor.energy_gas_cost": {
|
||||
entity_id: "sensor.energy_gas_cost",
|
||||
state: "2",
|
||||
attributes: {
|
||||
last_reset: "1970-01-01T00:00:00:00+00",
|
||||
unit_of_measurement: "EUR",
|
||||
},
|
||||
},
|
||||
"sensor.energy_gas": {
|
||||
entity_id: "sensor.energy_gas",
|
||||
state: "4",
|
||||
attributes: {
|
||||
last_reset: "1970-01-01T00:00:00:00+00",
|
||||
friendly_name: "Gas",
|
||||
unit_of_measurement: "m³",
|
||||
},
|
||||
},
|
||||
"sensor.energy_car": {
|
||||
entity_id: "sensor.energy_car",
|
||||
state: "4",
|
||||
attributes: {
|
||||
last_reset: "1970-01-01T00:00:00:00+00",
|
||||
friendly_name: "Electric car",
|
||||
unit_of_measurement: "kWh",
|
||||
},
|
||||
},
|
||||
"sensor.energy_ac": {
|
||||
entity_id: "sensor.energy_ac",
|
||||
state: "3",
|
||||
attributes: {
|
||||
last_reset: "1970-01-01T00:00:00:00+00",
|
||||
friendly_name: "Air conditioning",
|
||||
unit_of_measurement: "kWh",
|
||||
},
|
||||
},
|
||||
"sensor.energy_washing_machine": {
|
||||
entity_id: "sensor.energy_washing_machine",
|
||||
state: "6",
|
||||
attributes: {
|
||||
last_reset: "1970-01-01T00:00:00:00+00",
|
||||
friendly_name: "Washing machine",
|
||||
unit_of_measurement: "kWh",
|
||||
},
|
||||
},
|
||||
"sensor.energy_dryer": {
|
||||
entity_id: "sensor.energy_dryer",
|
||||
state: "5.5",
|
||||
attributes: {
|
||||
last_reset: "1970-01-01T00:00:00:00+00",
|
||||
friendly_name: "Dryer",
|
||||
unit_of_measurement: "kWh",
|
||||
},
|
||||
},
|
||||
"sensor.energy_heat_pump": {
|
||||
entity_id: "sensor.energy_heat_pump",
|
||||
state: "6",
|
||||
attributes: {
|
||||
last_reset: "1970-01-01T00:00:00:00+00",
|
||||
friendly_name: "Heat pump",
|
||||
unit_of_measurement: "kWh",
|
||||
},
|
||||
},
|
||||
"sensor.energy_boiler": {
|
||||
entity_id: "sensor.energy_boiler",
|
||||
state: "7",
|
||||
attributes: {
|
||||
last_reset: "1970-01-01T00:00:00:00+00",
|
||||
friendly_name: "Boiler",
|
||||
unit_of_measurement: "kWh",
|
||||
},
|
||||
},
|
||||
});
|
@@ -1,6 +1,4 @@
|
||||
import { addHours, differenceInHours, endOfDay } from "date-fns";
|
||||
import { HassEntity } from "home-assistant-js-websocket";
|
||||
import { StatisticValue } from "../../../src/data/history";
|
||||
import { MockHomeAssistant } from "../../../src/fake_data/provide_hass";
|
||||
|
||||
interface HistoryQueryParams {
|
||||
@@ -66,219 +64,17 @@ const generateHistory = (state, deltas) => {
|
||||
|
||||
const incrementalUnits = ["clients", "queries", "ads"];
|
||||
|
||||
const generateMeanStatistics = (
|
||||
id: string,
|
||||
start: Date,
|
||||
end: Date,
|
||||
initValue: number,
|
||||
maxDiff: number
|
||||
) => {
|
||||
const statistics: StatisticValue[] = [];
|
||||
let currentDate = new Date(start);
|
||||
currentDate.setMinutes(0, 0, 0);
|
||||
let lastVal = initValue;
|
||||
const now = new Date();
|
||||
while (end > currentDate && currentDate < now) {
|
||||
const delta = Math.random() * maxDiff;
|
||||
const mean = lastVal + delta;
|
||||
statistics.push({
|
||||
statistic_id: id,
|
||||
start: currentDate.toISOString(),
|
||||
mean,
|
||||
min: mean - Math.random() * maxDiff,
|
||||
max: mean + Math.random() * maxDiff,
|
||||
last_reset: "1970-01-01T00:00:00+00:00",
|
||||
state: mean,
|
||||
sum: null,
|
||||
});
|
||||
lastVal = mean;
|
||||
currentDate = addHours(currentDate, 1);
|
||||
}
|
||||
return statistics;
|
||||
};
|
||||
|
||||
const generateSumStatistics = (
|
||||
id: string,
|
||||
start: Date,
|
||||
end: Date,
|
||||
initValue: number,
|
||||
maxDiff: number
|
||||
) => {
|
||||
const statistics: StatisticValue[] = [];
|
||||
let currentDate = new Date(start);
|
||||
currentDate.setMinutes(0, 0, 0);
|
||||
let sum = initValue;
|
||||
const now = new Date();
|
||||
while (end > currentDate && currentDate < now) {
|
||||
const add = Math.random() * maxDiff;
|
||||
sum += add;
|
||||
statistics.push({
|
||||
statistic_id: id,
|
||||
start: currentDate.toISOString(),
|
||||
mean: null,
|
||||
min: null,
|
||||
max: null,
|
||||
last_reset: "1970-01-01T00:00:00+00:00",
|
||||
state: initValue + sum,
|
||||
sum,
|
||||
});
|
||||
currentDate = addHours(currentDate, 1);
|
||||
}
|
||||
return statistics;
|
||||
};
|
||||
|
||||
const generateCurvedStatistics = (
|
||||
id: string,
|
||||
start: Date,
|
||||
end: Date,
|
||||
initValue: number,
|
||||
maxDiff: number,
|
||||
metered: boolean
|
||||
) => {
|
||||
const statistics: StatisticValue[] = [];
|
||||
let currentDate = new Date(start);
|
||||
currentDate.setMinutes(0, 0, 0);
|
||||
let sum = initValue;
|
||||
const hours = differenceInHours(end, start) - 1;
|
||||
let i = 0;
|
||||
let half = false;
|
||||
const now = new Date();
|
||||
while (end > currentDate && currentDate < now) {
|
||||
const add = Math.random() * maxDiff;
|
||||
sum += i * add;
|
||||
statistics.push({
|
||||
statistic_id: id,
|
||||
start: currentDate.toISOString(),
|
||||
mean: null,
|
||||
min: null,
|
||||
max: null,
|
||||
last_reset: "1970-01-01T00:00:00+00:00",
|
||||
state: initValue + sum,
|
||||
sum: metered ? sum : null,
|
||||
});
|
||||
currentDate = addHours(currentDate, 1);
|
||||
if (!half && i > hours / 2) {
|
||||
half = true;
|
||||
}
|
||||
i += half ? -1 : 1;
|
||||
}
|
||||
return statistics;
|
||||
};
|
||||
|
||||
const statisticsFunctions: Record<
|
||||
string,
|
||||
(id: string, start: Date, end: Date) => StatisticValue[]
|
||||
> = {
|
||||
"sensor.energy_consumption_tarif_1": (id: string, start: Date, end: Date) => {
|
||||
const morningEnd = new Date(start.getTime() + 10 * 60 * 60 * 1000);
|
||||
const morningLow = generateSumStatistics(id, start, morningEnd, 0, 0.7);
|
||||
const eveningStart = new Date(start.getTime() + 20 * 60 * 60 * 1000);
|
||||
const morningFinalVal = morningLow.length
|
||||
? morningLow[morningLow.length - 1].sum!
|
||||
: 0;
|
||||
const empty = generateSumStatistics(
|
||||
id,
|
||||
morningEnd,
|
||||
eveningStart,
|
||||
morningFinalVal,
|
||||
0
|
||||
);
|
||||
const eveningLow = generateSumStatistics(
|
||||
id,
|
||||
eveningStart,
|
||||
end,
|
||||
morningFinalVal,
|
||||
0.7
|
||||
);
|
||||
return [...morningLow, ...empty, ...eveningLow];
|
||||
},
|
||||
"sensor.energy_consumption_tarif_2": (id: string, start: Date, end: Date) => {
|
||||
const morningEnd = new Date(start.getTime() + 9 * 60 * 60 * 1000);
|
||||
const eveningStart = new Date(start.getTime() + 20 * 60 * 60 * 1000);
|
||||
const highTarif = generateSumStatistics(
|
||||
id,
|
||||
morningEnd,
|
||||
eveningStart,
|
||||
0,
|
||||
0.3
|
||||
);
|
||||
const highTarifFinalVal = highTarif.length
|
||||
? highTarif[highTarif.length - 1].sum!
|
||||
: 0;
|
||||
const morning = generateSumStatistics(id, start, morningEnd, 0, 0);
|
||||
const evening = generateSumStatistics(
|
||||
id,
|
||||
eveningStart,
|
||||
end,
|
||||
highTarifFinalVal,
|
||||
0
|
||||
);
|
||||
return [...morning, ...highTarif, ...evening];
|
||||
},
|
||||
"sensor.energy_production_tarif_1": (id, start, end) =>
|
||||
generateSumStatistics(id, start, end, 0, 0),
|
||||
"sensor.energy_production_tarif_1_compensation": (id, start, end) =>
|
||||
generateSumStatistics(id, start, end, 0, 0),
|
||||
"sensor.energy_production_tarif_2": (id, start, end) => {
|
||||
const productionStart = new Date(start.getTime() + 9 * 60 * 60 * 1000);
|
||||
const productionEnd = new Date(start.getTime() + 21 * 60 * 60 * 1000);
|
||||
const dayEnd = new Date(endOfDay(productionEnd));
|
||||
const production = generateCurvedStatistics(
|
||||
id,
|
||||
productionStart,
|
||||
productionEnd,
|
||||
0,
|
||||
0.15,
|
||||
true
|
||||
);
|
||||
const productionFinalVal = production.length
|
||||
? production[production.length - 1].sum!
|
||||
: 0;
|
||||
const morning = generateSumStatistics(id, start, productionStart, 0, 0);
|
||||
const evening = generateSumStatistics(
|
||||
id,
|
||||
productionEnd,
|
||||
dayEnd,
|
||||
productionFinalVal,
|
||||
0
|
||||
);
|
||||
const rest = generateSumStatistics(id, dayEnd, end, productionFinalVal, 1);
|
||||
return [...morning, ...production, ...evening, ...rest];
|
||||
},
|
||||
"sensor.solar_production": (id, start, end) => {
|
||||
const productionStart = new Date(start.getTime() + 7 * 60 * 60 * 1000);
|
||||
const productionEnd = new Date(start.getTime() + 23 * 60 * 60 * 1000);
|
||||
const dayEnd = new Date(endOfDay(productionEnd));
|
||||
const production = generateCurvedStatistics(
|
||||
id,
|
||||
productionStart,
|
||||
productionEnd,
|
||||
0,
|
||||
0.3,
|
||||
true
|
||||
);
|
||||
const productionFinalVal = production.length
|
||||
? production[production.length - 1].sum!
|
||||
: 0;
|
||||
const morning = generateSumStatistics(id, start, productionStart, 0, 0);
|
||||
const evening = generateSumStatistics(
|
||||
id,
|
||||
productionEnd,
|
||||
dayEnd,
|
||||
productionFinalVal,
|
||||
0
|
||||
);
|
||||
const rest = generateSumStatistics(id, dayEnd, end, productionFinalVal, 2);
|
||||
return [...morning, ...production, ...evening, ...rest];
|
||||
},
|
||||
"sensor.grid_fossil_fuel_percentage": (id, start, end) =>
|
||||
generateMeanStatistics(id, start, end, 35, 1.3),
|
||||
};
|
||||
|
||||
export const mockHistory = (mockHass: MockHomeAssistant) => {
|
||||
mockHass.mockAPI(
|
||||
new RegExp("history/period/.+"),
|
||||
(hass, _method, path, _parameters) => {
|
||||
(
|
||||
hass,
|
||||
// @ts-ignore
|
||||
method,
|
||||
path,
|
||||
// @ts-ignore
|
||||
parameters
|
||||
) => {
|
||||
const params = parseQuery<HistoryQueryParams>(path.split("?")[1]);
|
||||
const entities = params.filter_entity_id.split(",");
|
||||
|
||||
@@ -299,7 +95,7 @@ export const mockHistory = (mockHass: MockHomeAssistant) => {
|
||||
const numberState = Number(state.state);
|
||||
|
||||
if (isNaN(numberState)) {
|
||||
// eslint-disable-next-line no-console
|
||||
// eslint-disable-next-line
|
||||
console.log(
|
||||
"Ignoring state with unparsable state but with a unit",
|
||||
entityId,
|
||||
@@ -344,40 +140,4 @@ export const mockHistory = (mockHass: MockHomeAssistant) => {
|
||||
return results;
|
||||
}
|
||||
);
|
||||
mockHass.mockWS("history/list_statistic_ids", () => []);
|
||||
mockHass.mockWS(
|
||||
"history/statistics_during_period",
|
||||
({ statistic_ids, start_time, end_time }, hass) => {
|
||||
const start = new Date(start_time);
|
||||
const end = end_time ? new Date(end_time) : new Date();
|
||||
|
||||
const statistics: Record<string, StatisticValue[]> = {};
|
||||
|
||||
statistic_ids.forEach((id: string) => {
|
||||
if (id in statisticsFunctions) {
|
||||
statistics[id] = statisticsFunctions[id](id, start, end);
|
||||
} else {
|
||||
const entityState = hass.states[id];
|
||||
const state = entityState ? Number(entityState.state) : 1;
|
||||
statistics[id] =
|
||||
entityState && "last_reset" in entityState.attributes
|
||||
? generateSumStatistics(
|
||||
id,
|
||||
start,
|
||||
end,
|
||||
state,
|
||||
state * (state > 80 ? 0.01 : 0.05)
|
||||
)
|
||||
: generateMeanStatistics(
|
||||
id,
|
||||
start,
|
||||
end,
|
||||
state,
|
||||
state * (state > 80 ? 0.05 : 0.1)
|
||||
);
|
||||
}
|
||||
});
|
||||
return statistics;
|
||||
}
|
||||
);
|
||||
};
|
||||
|
@@ -10,9 +10,10 @@ export const mockLovelace = (
|
||||
localizePromise: Promise<LocalizeFunc>
|
||||
) => {
|
||||
hass.mockWS("lovelace/config", () =>
|
||||
Promise.all([selectedDemoConfig, localizePromise]).then(
|
||||
([config, localize]) => config.lovelace(localize)
|
||||
)
|
||||
Promise.all([
|
||||
selectedDemoConfig,
|
||||
localizePromise,
|
||||
]).then(([config, localize]) => config.lovelace(localize))
|
||||
);
|
||||
|
||||
hass.mockWS("lovelace/config/save", () => Promise.resolve());
|
||||
@@ -23,9 +24,9 @@ customElements.whenDefined("hui-view").then(() => {
|
||||
// eslint-disable-next-line
|
||||
const HUIView = customElements.get("hui-view");
|
||||
// Patch HUI-VIEW to make the lovelace object available to the demo card
|
||||
const oldCreateCard = HUIView!.prototype.createCardElement;
|
||||
const oldCreateCard = HUIView.prototype.createCardElement;
|
||||
|
||||
HUIView!.prototype.createCardElement = function (config) {
|
||||
HUIView.prototype.createCardElement = function (config) {
|
||||
const el = oldCreateCard.call(this, config);
|
||||
if (el.tagName === "HA-DEMO-CARD") {
|
||||
(el as HADemoCard).lovelace = this.lovelace;
|
||||
|
@@ -6,7 +6,7 @@ export const mockTemplate = (hass: MockHomeAssistant) => {
|
||||
body: { message: "Template dev tool does not work in the demo." },
|
||||
})
|
||||
);
|
||||
hass.mockWS("render_template", (msg, _hass, onChange) => {
|
||||
hass.mockWS("render_template", (msg, onChange) => {
|
||||
onChange!({
|
||||
result: msg.template,
|
||||
listeners: { all: false, domains: [], entities: [], time: false },
|
||||
|
@@ -1,4 +1,3 @@
|
||||
/* eslint-disable lit/no-template-arrow */
|
||||
import { html, css, LitElement, TemplateResult } from "lit";
|
||||
import { customElement, property } from "lit/decorators";
|
||||
import "../../../src/components/ha-card";
|
||||
|
@@ -1,14 +1,13 @@
|
||||
/* eslint-disable lit/no-template-arrow */
|
||||
import { html, css, LitElement, TemplateResult } from "lit";
|
||||
import "../../../src/components/ha-card";
|
||||
import "../../../src/components/trace/hat-script-graph";
|
||||
import "../../../src/components/trace/hat-trace-timeline";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { provideHass } from "../../../src/fake_data/provide_hass";
|
||||
import { HomeAssistant } from "../../../src/types";
|
||||
import { DemoTrace } from "../data/traces/types";
|
||||
import { basicTrace } from "../data/traces/basic_trace";
|
||||
import { motionLightTrace } from "../data/traces/motion-light-trace";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
|
||||
const traces: DemoTrace[] = [basicTrace, motionLightTrace];
|
||||
|
||||
|
@@ -1,150 +0,0 @@
|
||||
import { html, css, LitElement, TemplateResult } from "lit";
|
||||
import { customElement } from "lit/decorators";
|
||||
import "../../../src/components/ha-alert";
|
||||
import "../../../src/components/ha-card";
|
||||
|
||||
const alerts: {
|
||||
title?: string;
|
||||
description: string | TemplateResult;
|
||||
type: "info" | "warning" | "error" | "success";
|
||||
dismissable?: boolean;
|
||||
action?: string;
|
||||
rtl?: boolean;
|
||||
}[] = [
|
||||
{
|
||||
title: "Test info alert",
|
||||
description: "This is a test info alert with a title and description",
|
||||
type: "info",
|
||||
},
|
||||
{
|
||||
title: "Test warning alert",
|
||||
description: "This is a test warning alert with a title and description",
|
||||
type: "warning",
|
||||
},
|
||||
{
|
||||
title: "Test error alert",
|
||||
description: "This is a test error alert with a title and description",
|
||||
type: "error",
|
||||
},
|
||||
{
|
||||
title: "Test warning with long string",
|
||||
description:
|
||||
"sensor.lorem_ipsum_lorem_ipsum_lorem_ipsum_lorem_ipsum_lorem_ipsum_lorem_ipsum_lorem_ipsum_lorem_ipsum_lorem_ipsum_lorem_ipsum_lorem_ipsum_lorem_ipsum",
|
||||
type: "warning",
|
||||
},
|
||||
{
|
||||
title: "Test success alert",
|
||||
description: "This is a test success alert with a title and description",
|
||||
type: "success",
|
||||
},
|
||||
{
|
||||
description: "This is a test info alert with description only",
|
||||
type: "info",
|
||||
},
|
||||
{
|
||||
description:
|
||||
"This is a test warning alert with a rally really really rally really really rally really really rally really really rally really really rally really really rally really really rally really really rally really really rally really really rally really really rally really really rally really really rally really really rally really really rally really really rally really really rally really really rally really really rally really really rally really really long description only",
|
||||
type: "warning",
|
||||
},
|
||||
{
|
||||
title: "Error with description and list",
|
||||
description: html`<p>
|
||||
This is a test error alert with a title, description and a list
|
||||
</p>
|
||||
<ul>
|
||||
<li>List item #1</li>
|
||||
<li>List item #2</li>
|
||||
<li>List item #3</li>
|
||||
</ul>`,
|
||||
type: "error",
|
||||
},
|
||||
{
|
||||
title: "Test dismissable alert",
|
||||
description: "This is a test success alert that can be dismissable",
|
||||
type: "success",
|
||||
dismissable: true,
|
||||
},
|
||||
{
|
||||
description: "Dismissable information",
|
||||
type: "info",
|
||||
dismissable: true,
|
||||
},
|
||||
{
|
||||
title: "Error with action",
|
||||
description: "This is a test error alert with action",
|
||||
type: "error",
|
||||
action: "restart",
|
||||
},
|
||||
{
|
||||
title: "Unsaved data",
|
||||
description: "You have unsaved data",
|
||||
type: "warning",
|
||||
action: "save",
|
||||
},
|
||||
{
|
||||
description: "Dismissable information (RTL)",
|
||||
type: "info",
|
||||
dismissable: true,
|
||||
rtl: true,
|
||||
},
|
||||
{
|
||||
title: "Error with action",
|
||||
description: "This is a test error alert with action (RTL)",
|
||||
type: "error",
|
||||
action: "restart",
|
||||
rtl: true,
|
||||
},
|
||||
{
|
||||
title: "Test success alert (RTL)",
|
||||
description: "This is a test success alert with a title and description",
|
||||
type: "success",
|
||||
rtl: true,
|
||||
},
|
||||
];
|
||||
|
||||
@customElement("demo-ha-alert")
|
||||
export class DemoHaAlert extends LitElement {
|
||||
protected render(): TemplateResult {
|
||||
return html`
|
||||
<ha-card header="ha-alert demo">
|
||||
${alerts.map(
|
||||
(alert) => html`
|
||||
<ha-alert
|
||||
.title=${alert.title || ""}
|
||||
.alertType=${alert.type}
|
||||
.dismissable=${alert.dismissable || false}
|
||||
.actionText=${alert.action || ""}
|
||||
.rtl=${alert.rtl || false}
|
||||
>
|
||||
${alert.description}
|
||||
</ha-alert>
|
||||
`
|
||||
)}
|
||||
</ha-card>
|
||||
`;
|
||||
}
|
||||
|
||||
static get styles() {
|
||||
return css`
|
||||
ha-card {
|
||||
max-width: 600px;
|
||||
margin: 24px auto;
|
||||
}
|
||||
.condition {
|
||||
padding: 16px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
span {
|
||||
margin-right: 16px;
|
||||
}
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"demo-ha-alert": DemoHaAlert;
|
||||
}
|
||||
}
|
@@ -2,8 +2,6 @@ import { html, css, LitElement, TemplateResult } from "lit";
|
||||
import "../../../src/components/ha-formfield";
|
||||
import "../../../src/components/ha-switch";
|
||||
|
||||
import { classMap } from "lit/directives/class-map";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { IntegrationManifest } from "../../../src/data/integration";
|
||||
|
||||
import { provideHass } from "../../../src/fake_data/provide_hass";
|
||||
@@ -17,6 +15,8 @@ import type {
|
||||
} from "../../../src/panels/config/integrations/ha-config-integrations";
|
||||
import { DeviceRegistryEntry } from "../../../src/data/device_registry";
|
||||
import { EntityRegistryEntry } from "../../../src/data/entity_registry";
|
||||
import { classMap } from "lit/directives/class-map";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
|
||||
const createConfigEntry = (
|
||||
title: string,
|
||||
@@ -28,11 +28,10 @@ const createConfigEntry = (
|
||||
title,
|
||||
source: "zeroconf",
|
||||
state: "loaded",
|
||||
connection_class: "local_push",
|
||||
supports_options: false,
|
||||
supports_unload: true,
|
||||
disabled_by: null,
|
||||
pref_disable_new_entities: false,
|
||||
pref_disable_polling: false,
|
||||
reason: null,
|
||||
...override,
|
||||
});
|
||||
@@ -65,9 +64,6 @@ const configPanelEntry = createConfigEntry("Config Panel", {
|
||||
const optionsFlowEntry = createConfigEntry("Options Flow", {
|
||||
supports_options: true,
|
||||
});
|
||||
const disabledPollingEntry = createConfigEntry("Disabled Polling", {
|
||||
pref_disable_polling: true,
|
||||
});
|
||||
const setupErrorEntry = createConfigEntry("Setup Error", {
|
||||
state: "setup_error",
|
||||
});
|
||||
@@ -140,7 +136,6 @@ const configEntries: Array<{
|
||||
{ items: [loadedEntry] },
|
||||
{ items: [configPanelEntry] },
|
||||
{ items: [optionsFlowEntry] },
|
||||
{ items: [disabledPollingEntry] },
|
||||
{ items: [nameAsDomainEntry] },
|
||||
{ items: [longNameEntry] },
|
||||
{ items: [longNonBreakingNameEntry] },
|
||||
|
@@ -1,5 +1,5 @@
|
||||
import "@material/mwc-button";
|
||||
import { css, html, LitElement, TemplateResult } from "lit";
|
||||
import { html, LitElement, TemplateResult } from "lit";
|
||||
import { customElement } from "lit/decorators";
|
||||
import "../../../src/components/ha-card";
|
||||
import { ActionHandlerEvent } from "../../../src/data/lovelace";
|
||||
@@ -9,6 +9,7 @@ import { actionHandler } from "../../../src/panels/lovelace/common/directives/ac
|
||||
export class DemoUtilLongPress extends LitElement {
|
||||
protected render(): TemplateResult {
|
||||
return html`
|
||||
${this.renderStyle()}
|
||||
${[1, 2, 3].map(
|
||||
() => html`
|
||||
<ha-card>
|
||||
@@ -40,22 +41,26 @@ export class DemoUtilLongPress extends LitElement {
|
||||
area.scrollTop = area.scrollHeight;
|
||||
}
|
||||
|
||||
static styles = css`
|
||||
ha-card {
|
||||
width: 200px;
|
||||
margin: calc(42vh - 140px) auto;
|
||||
padding: 8px;
|
||||
text-align: center;
|
||||
}
|
||||
ha-card:first-of-type {
|
||||
margin-top: 16px;
|
||||
}
|
||||
ha-card:last-of-type {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
private renderStyle() {
|
||||
return html`
|
||||
<style>
|
||||
ha-card {
|
||||
width: 200px;
|
||||
margin: calc(42vh - 140px) auto;
|
||||
padding: 8px;
|
||||
text-align: center;
|
||||
}
|
||||
ha-card:first-of-type {
|
||||
margin-top: 16px;
|
||||
}
|
||||
ha-card:last-of-type {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
textarea {
|
||||
height: 50px;
|
||||
}
|
||||
`;
|
||||
textarea {
|
||||
height: 50px;
|
||||
}
|
||||
</style>
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
@@ -172,14 +172,6 @@ class HaGallery extends PolymerElement {
|
||||
this.$.notifications.showDialog({ message: ev.detail.message })
|
||||
);
|
||||
|
||||
this.addEventListener("alert-dismissed-clicked", () =>
|
||||
this.$.notifications.showDialog({ message: "Alert dismissed clicked" })
|
||||
);
|
||||
|
||||
this.addEventListener("alert-action-clicked", () =>
|
||||
this.$.notifications.showDialog({ message: "Alert action clicked" })
|
||||
);
|
||||
|
||||
this.addEventListener("hass-more-info", (ev) => {
|
||||
if (ev.detail.entityId) {
|
||||
this.$.notifications.showDialog({
|
||||
|
@@ -13,7 +13,6 @@ import {
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import "web-animations-js/web-animations-next-lite.min";
|
||||
import "../../../../src/components/buttons/ha-progress-button";
|
||||
import "../../../../src/components/ha-alert";
|
||||
import "../../../../src/components/ha-card";
|
||||
import {
|
||||
HassioAddonDetails,
|
||||
@@ -54,9 +53,7 @@ class HassioAddonAudio extends LitElement {
|
||||
.header=${this.supervisor.localize("addon.configuration.audio.header")}
|
||||
>
|
||||
<div class="card-content">
|
||||
${this._error
|
||||
? html`<ha-alert alert-type="error">${this._error}</ha-alert>`
|
||||
: ""}
|
||||
${this._error ? html` <div class="errors">${this._error}</div> ` : ""}
|
||||
|
||||
<paper-dropdown-menu
|
||||
.label=${this.supervisor.localize(
|
||||
@@ -120,6 +117,10 @@ class HassioAddonAudio extends LitElement {
|
||||
paper-dropdown-menu {
|
||||
display: block;
|
||||
}
|
||||
.errors {
|
||||
color: var(--error-color);
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
paper-item {
|
||||
width: 450px;
|
||||
}
|
||||
|
@@ -2,6 +2,7 @@ import "@material/mwc-button";
|
||||
import { ActionDetail } from "@material/mwc-list";
|
||||
import "@material/mwc-list/mwc-list-item";
|
||||
import { mdiDotsVertical } from "@mdi/js";
|
||||
import "@polymer/iron-autogrow-textarea/iron-autogrow-textarea";
|
||||
import { DEFAULT_SCHEMA, Type } from "js-yaml";
|
||||
import {
|
||||
css,
|
||||
@@ -17,7 +18,6 @@ import { fireEvent } from "../../../../src/common/dom/fire_event";
|
||||
import "../../../../src/components/buttons/ha-progress-button";
|
||||
import "../../../../src/components/ha-button-menu";
|
||||
import "../../../../src/components/ha-card";
|
||||
import "../../../../src/components/ha-alert";
|
||||
import "../../../../src/components/ha-form/ha-form";
|
||||
import type { HaFormSchema } from "../../../../src/components/ha-form/ha-form";
|
||||
import "../../../../src/components/ha-formfield";
|
||||
@@ -134,21 +134,19 @@ class HassioAddonConfig extends LitElement {
|
||||
></ha-form>`
|
||||
: html` <ha-yaml-editor
|
||||
@value-changed=${this._configChanged}
|
||||
.yamlSchema=${ADDON_YAML_SCHEMA}
|
||||
.schema=${ADDON_YAML_SCHEMA}
|
||||
></ha-yaml-editor>`}
|
||||
${this._error
|
||||
? html`<ha-alert alert-type="error">${this._error}</ha-alert>`
|
||||
: ""}
|
||||
${this._error ? html` <div class="errors">${this._error}</div> ` : ""}
|
||||
${!this._yamlMode ||
|
||||
(this._canShowSchema && this.addon.schema) ||
|
||||
this._valid
|
||||
? ""
|
||||
: html`
|
||||
<ha-alert alert-type="error">
|
||||
<div class="errors">
|
||||
${this.supervisor.localize(
|
||||
"addon.configuration.options.invalid_yaml"
|
||||
)}
|
||||
</ha-alert>
|
||||
</div>
|
||||
`}
|
||||
</div>
|
||||
${hasHiddenOptions
|
||||
@@ -259,7 +257,7 @@ class HassioAddonConfig extends LitElement {
|
||||
path: "options",
|
||||
};
|
||||
fireEvent(this, "hass-api-called", eventdata);
|
||||
} catch (err: any) {
|
||||
} catch (err) {
|
||||
this._error = this.supervisor.localize(
|
||||
"addon.common.update_available",
|
||||
"error",
|
||||
@@ -271,9 +269,6 @@ class HassioAddonConfig extends LitElement {
|
||||
|
||||
private async _saveTapped(ev: CustomEvent): Promise<void> {
|
||||
const button = ev.currentTarget as any;
|
||||
const options: Record<string, unknown> = this._yamlMode
|
||||
? this._editor?.value
|
||||
: this._options;
|
||||
const eventdata = {
|
||||
success: true,
|
||||
response: undefined,
|
||||
@@ -287,20 +282,20 @@ class HassioAddonConfig extends LitElement {
|
||||
const validation = await validateHassioAddonOption(
|
||||
this.hass,
|
||||
this.addon.slug,
|
||||
options
|
||||
this._editor?.value
|
||||
);
|
||||
if (!validation.valid) {
|
||||
throw Error(validation.message);
|
||||
}
|
||||
await setHassioAddonOption(this.hass, this.addon.slug, {
|
||||
options,
|
||||
options: this._yamlMode ? this._editor?.value : this._options,
|
||||
});
|
||||
|
||||
this._configHasChanged = false;
|
||||
if (this.addon?.state === "started") {
|
||||
await suggestAddonRestart(this, this.hass, this.supervisor, this.addon);
|
||||
}
|
||||
} catch (err: any) {
|
||||
} catch (err) {
|
||||
this._error = this.supervisor.localize(
|
||||
"addon.failed_to_save",
|
||||
"error",
|
||||
@@ -327,7 +322,17 @@ class HassioAddonConfig extends LitElement {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.errors {
|
||||
color: var(--error-color);
|
||||
margin-top: 16px;
|
||||
}
|
||||
iron-autogrow-textarea {
|
||||
width: 100%;
|
||||
font-family: var(--code-font-family, monospace);
|
||||
}
|
||||
.syntaxerror {
|
||||
color: var(--error-color);
|
||||
}
|
||||
.card-menu {
|
||||
float: right;
|
||||
z-index: 3;
|
||||
|
@@ -10,7 +10,6 @@ import {
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { fireEvent } from "../../../../src/common/dom/fire_event";
|
||||
import "../../../../src/components/buttons/ha-progress-button";
|
||||
import "../../../../src/components/ha-alert";
|
||||
import "../../../../src/components/ha-card";
|
||||
import {
|
||||
HassioAddonDetails,
|
||||
@@ -63,9 +62,7 @@ class HassioAddonNetwork extends LitElement {
|
||||
)}
|
||||
>
|
||||
<div class="card-content">
|
||||
${this._error
|
||||
? html`<ha-alert alert-type="error">${this._error}</ha-alert>`
|
||||
: ""}
|
||||
${this._error ? html` <div class="errors">${this._error}</div> ` : ""}
|
||||
|
||||
<table>
|
||||
<tbody>
|
||||
@@ -89,9 +86,9 @@ class HassioAddonNetwork extends LitElement {
|
||||
<td>
|
||||
<paper-input
|
||||
@value-changed=${this._configChanged}
|
||||
placeholder=${this.supervisor.localize(
|
||||
placeholder="${this.supervisor.localize(
|
||||
"addon.configuration.network.disabled"
|
||||
)}
|
||||
)}"
|
||||
.value=${item.host ? String(item.host) : ""}
|
||||
.container=${item.container}
|
||||
no-label-float
|
||||
@@ -171,7 +168,7 @@ class HassioAddonNetwork extends LitElement {
|
||||
if (this.addon?.state === "started") {
|
||||
await suggestAddonRestart(this, this.hass, this.supervisor, this.addon);
|
||||
}
|
||||
} catch (err: any) {
|
||||
} catch (err) {
|
||||
this._error = this.supervisor.localize(
|
||||
"addon.failed_to_reset",
|
||||
"error",
|
||||
@@ -207,7 +204,7 @@ class HassioAddonNetwork extends LitElement {
|
||||
if (this.addon?.state === "started") {
|
||||
await suggestAddonRestart(this, this.hass, this.supervisor, this.addon);
|
||||
}
|
||||
} catch (err: any) {
|
||||
} catch (err) {
|
||||
this._error = this.supervisor.localize(
|
||||
"addon.failed_to_save",
|
||||
"error",
|
||||
@@ -228,6 +225,10 @@ class HassioAddonNetwork extends LitElement {
|
||||
ha-card {
|
||||
display: block;
|
||||
}
|
||||
.errors {
|
||||
color: var(--error-color);
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
.card-actions {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
|
@@ -1,9 +1,7 @@
|
||||
import "../../../../src/components/ha-card";
|
||||
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
||||
import "../../../../src/components/ha-alert";
|
||||
import "../../../../src/components/ha-circular-progress";
|
||||
import "../../../../src/components/ha-markdown";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import {
|
||||
fetchHassioAddonDocumentation,
|
||||
HassioAddonDetails,
|
||||
@@ -14,6 +12,7 @@ import { haStyle } from "../../../../src/resources/styles";
|
||||
import { HomeAssistant } from "../../../../src/types";
|
||||
import { hassioStyle } from "../../resources/hassio-style";
|
||||
import { Supervisor } from "../../../../src/data/supervisor/supervisor";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
|
||||
@customElement("hassio-addon-documentation-tab")
|
||||
class HassioAddonDocumentationDashboard extends LitElement {
|
||||
@@ -39,9 +38,7 @@ class HassioAddonDocumentationDashboard extends LitElement {
|
||||
return html`
|
||||
<div class="content">
|
||||
<ha-card>
|
||||
${this._error
|
||||
? html`<ha-alert alert-type="error">${this._error}</ha-alert>`
|
||||
: ""}
|
||||
${this._error ? html` <div class="errors">${this._error}</div> ` : ""}
|
||||
<div class="card-content">
|
||||
${this._content
|
||||
? html`<ha-markdown .content=${this._content}></ha-markdown>`
|
||||
@@ -79,7 +76,7 @@ class HassioAddonDocumentationDashboard extends LitElement {
|
||||
this.hass,
|
||||
this.addon!.slug
|
||||
);
|
||||
} catch (err: any) {
|
||||
} catch (err) {
|
||||
this._error = this.supervisor.localize(
|
||||
"addon.documentation.get_logs",
|
||||
"error",
|
||||
|
@@ -222,7 +222,7 @@ class HassioAddonDashboard extends LitElement {
|
||||
try {
|
||||
const addoninfo = await fetchHassioAddonInfo(this.hass, addon);
|
||||
this.addon = addoninfo;
|
||||
} catch (err: any) {
|
||||
} catch (err) {
|
||||
this._error = `Error fetching addon info: ${extractApiErrorMessage(err)}`;
|
||||
this.addon = undefined;
|
||||
}
|
||||
|
@@ -23,7 +23,6 @@ import { fireEvent } from "../../../../src/common/dom/fire_event";
|
||||
import { navigate } from "../../../../src/common/navigate";
|
||||
import "../../../../src/components/buttons/ha-call-api-button";
|
||||
import "../../../../src/components/buttons/ha-progress-button";
|
||||
import "../../../../src/components/ha-alert";
|
||||
import "../../../../src/components/ha-card";
|
||||
import "../../../../src/components/ha-label-badge";
|
||||
import "../../../../src/components/ha-markdown";
|
||||
@@ -123,18 +122,18 @@ class HassioAddonInfo extends LitElement {
|
||||
<div class="card-content">
|
||||
<hassio-card-content
|
||||
.hass=${this.hass}
|
||||
.title=${this.supervisor.localize(
|
||||
.title="${this.supervisor.localize(
|
||||
"addon.dashboard.new_update_available",
|
||||
"name",
|
||||
this.addon.name,
|
||||
"version",
|
||||
this.addon.version_latest
|
||||
)}
|
||||
.description=${this.supervisor.localize(
|
||||
)}"
|
||||
.description="${this.supervisor.localize(
|
||||
"common.running_version",
|
||||
"version",
|
||||
this.addon.version
|
||||
)}
|
||||
)}"
|
||||
icon=${mdiArrowUpBoldCircle}
|
||||
iconClass="update"
|
||||
></hassio-card-content>
|
||||
@@ -144,14 +143,14 @@ class HassioAddonInfo extends LitElement {
|
||||
this.addon.arch
|
||||
)
|
||||
? html`
|
||||
<ha-alert alert-type="warning">
|
||||
<p class="warning">
|
||||
${this.supervisor.localize(
|
||||
"addon.dashboard.not_available_arch"
|
||||
)}
|
||||
</ha-alert>
|
||||
</p>
|
||||
`
|
||||
: html`
|
||||
<ha-alert alert-type="warning">
|
||||
<p class="warning">
|
||||
${this.supervisor.localize(
|
||||
"addon.dashboard.not_available_arch",
|
||||
"core_version_installed",
|
||||
@@ -159,7 +158,7 @@ class HassioAddonInfo extends LitElement {
|
||||
"core_version_needed",
|
||||
addonStoreInfo.homeassistant
|
||||
)}
|
||||
</ha-alert>
|
||||
</p>
|
||||
`
|
||||
: ""}
|
||||
</div>
|
||||
@@ -254,7 +253,7 @@ class HassioAddonInfo extends LitElement {
|
||||
${this.supervisor.localize(
|
||||
"addon.dashboard.visit_addon_page",
|
||||
"name",
|
||||
html`<a href=${this.addon.url!} target="_blank" rel="noreferrer"
|
||||
html`<a href="${this.addon.url!}" target="_blank" rel="noreferrer"
|
||||
>${this.addon.name}</a
|
||||
>`
|
||||
)}
|
||||
@@ -437,10 +436,10 @@ class HassioAddonInfo extends LitElement {
|
||||
${this.addon.version
|
||||
? html`
|
||||
<div
|
||||
class=${classMap({
|
||||
class="${classMap({
|
||||
"addon-options": true,
|
||||
started: this.addon.state === "started",
|
||||
})}
|
||||
})}"
|
||||
>
|
||||
<ha-settings-row ?three-line=${this.narrow}>
|
||||
<span slot="heading">
|
||||
@@ -570,23 +569,21 @@ class HassioAddonInfo extends LitElement {
|
||||
: ""}
|
||||
</div>
|
||||
</div>
|
||||
${this._error
|
||||
? html`<ha-alert alert-type="error">${this._error}</ha-alert>`
|
||||
: ""}
|
||||
${this._error ? html` <div class="errors">${this._error}</div> ` : ""}
|
||||
${!this.addon.version && addonStoreInfo && !this.addon.available
|
||||
? !addonArchIsSupported(
|
||||
this.supervisor.info.supported_arch,
|
||||
this.addon.arch
|
||||
)
|
||||
? html`
|
||||
<ha-alert alert-type="warning">
|
||||
<p class="warning">
|
||||
${this.supervisor.localize(
|
||||
"addon.dashboard.not_available_arch"
|
||||
)}
|
||||
</ha-alert>
|
||||
</p>
|
||||
`
|
||||
: html`
|
||||
<ha-alert alert-type="warning">
|
||||
<p class="warning">
|
||||
${this.supervisor.localize(
|
||||
"addon.dashboard.not_available_version",
|
||||
"core_version_installed",
|
||||
@@ -594,7 +591,7 @@ class HassioAddonInfo extends LitElement {
|
||||
"core_version_needed",
|
||||
addonStoreInfo!.homeassistant
|
||||
)}
|
||||
</ha-alert>
|
||||
</p>
|
||||
`
|
||||
: ""}
|
||||
</div>
|
||||
@@ -796,7 +793,7 @@ class HassioAddonInfo extends LitElement {
|
||||
path: "option",
|
||||
};
|
||||
fireEvent(this, "hass-api-called", eventdata);
|
||||
} catch (err: any) {
|
||||
} catch (err) {
|
||||
this._error = this.supervisor.localize(
|
||||
"addon.failed_to_save",
|
||||
"error",
|
||||
@@ -818,7 +815,7 @@ class HassioAddonInfo extends LitElement {
|
||||
path: "option",
|
||||
};
|
||||
fireEvent(this, "hass-api-called", eventdata);
|
||||
} catch (err: any) {
|
||||
} catch (err) {
|
||||
this._error = this.supervisor.localize(
|
||||
"addon.failed_to_save",
|
||||
"error",
|
||||
@@ -840,7 +837,7 @@ class HassioAddonInfo extends LitElement {
|
||||
path: "option",
|
||||
};
|
||||
fireEvent(this, "hass-api-called", eventdata);
|
||||
} catch (err: any) {
|
||||
} catch (err) {
|
||||
this._error = this.supervisor.localize(
|
||||
"addon.failed_to_save",
|
||||
"error",
|
||||
@@ -862,7 +859,7 @@ class HassioAddonInfo extends LitElement {
|
||||
path: "security",
|
||||
};
|
||||
fireEvent(this, "hass-api-called", eventdata);
|
||||
} catch (err: any) {
|
||||
} catch (err) {
|
||||
this._error = this.supervisor.localize(
|
||||
"addon.failed_to_save",
|
||||
"error",
|
||||
@@ -884,7 +881,7 @@ class HassioAddonInfo extends LitElement {
|
||||
path: "option",
|
||||
};
|
||||
fireEvent(this, "hass-api-called", eventdata);
|
||||
} catch (err: any) {
|
||||
} catch (err) {
|
||||
this._error = this.supervisor.localize(
|
||||
"addon.failed_to_save",
|
||||
"error",
|
||||
@@ -895,24 +892,15 @@ class HassioAddonInfo extends LitElement {
|
||||
|
||||
private async _openChangelog(): Promise<void> {
|
||||
try {
|
||||
let content = await fetchHassioAddonChangelog(this.hass, this.addon.slug);
|
||||
if (
|
||||
content.includes(`# ${this.addon.version}`) &&
|
||||
content.includes(`# ${this.addon.version_latest}`)
|
||||
) {
|
||||
const newcontent = content.split(`# ${this.addon.version}`)[0];
|
||||
if (newcontent.includes(`# ${this.addon.version_latest}`)) {
|
||||
// Only change the content if the new version still exist
|
||||
// if the changelog does not have the newests version on top
|
||||
// this will not be true, and we don't modify the content
|
||||
content = newcontent;
|
||||
}
|
||||
}
|
||||
const content = await fetchHassioAddonChangelog(
|
||||
this.hass,
|
||||
this.addon.slug
|
||||
);
|
||||
showHassioMarkdownDialog(this, {
|
||||
title: this.supervisor.localize("addon.dashboard.changelog"),
|
||||
content,
|
||||
});
|
||||
} catch (err: any) {
|
||||
} catch (err) {
|
||||
showAlertDialog(this, {
|
||||
title: this.supervisor.localize(
|
||||
"addon.dashboard.action_error.get_changelog"
|
||||
@@ -934,7 +922,7 @@ class HassioAddonInfo extends LitElement {
|
||||
path: "install",
|
||||
};
|
||||
fireEvent(this, "hass-api-called", eventdata);
|
||||
} catch (err: any) {
|
||||
} catch (err) {
|
||||
showAlertDialog(this, {
|
||||
title: this.supervisor.localize("addon.dashboard.action_error.install"),
|
||||
text: extractApiErrorMessage(err),
|
||||
@@ -955,7 +943,7 @@ class HassioAddonInfo extends LitElement {
|
||||
path: "stop",
|
||||
};
|
||||
fireEvent(this, "hass-api-called", eventdata);
|
||||
} catch (err: any) {
|
||||
} catch (err) {
|
||||
showAlertDialog(this, {
|
||||
title: this.supervisor.localize("addon.dashboard.action_error.stop"),
|
||||
text: extractApiErrorMessage(err),
|
||||
@@ -976,7 +964,7 @@ class HassioAddonInfo extends LitElement {
|
||||
path: "stop",
|
||||
};
|
||||
fireEvent(this, "hass-api-called", eventdata);
|
||||
} catch (err: any) {
|
||||
} catch (err) {
|
||||
showAlertDialog(this, {
|
||||
title: this.supervisor.localize("addon.dashboard.action_error.restart"),
|
||||
text: extractApiErrorMessage(err),
|
||||
@@ -990,7 +978,7 @@ class HassioAddonInfo extends LitElement {
|
||||
supervisor: this.supervisor,
|
||||
name: this.addon.name,
|
||||
version: this.addon.version_latest,
|
||||
backupParams: {
|
||||
snapshotParams: {
|
||||
name: `addon_${this.addon.slug}_${this.addon.version}`,
|
||||
addons: [this.addon.slug],
|
||||
homeassistant: false,
|
||||
@@ -1035,7 +1023,7 @@ class HassioAddonInfo extends LitElement {
|
||||
button.progress = false;
|
||||
return;
|
||||
}
|
||||
} catch (err: any) {
|
||||
} catch (err) {
|
||||
showAlertDialog(this, {
|
||||
title: "Failed to validate addon configuration",
|
||||
text: extractApiErrorMessage(err),
|
||||
@@ -1053,7 +1041,7 @@ class HassioAddonInfo extends LitElement {
|
||||
path: "start",
|
||||
};
|
||||
fireEvent(this, "hass-api-called", eventdata);
|
||||
} catch (err: any) {
|
||||
} catch (err) {
|
||||
showAlertDialog(this, {
|
||||
title: this.supervisor.localize("addon.dashboard.action_error.start"),
|
||||
text: extractApiErrorMessage(err),
|
||||
@@ -1091,7 +1079,7 @@ class HassioAddonInfo extends LitElement {
|
||||
path: "uninstall",
|
||||
};
|
||||
fireEvent(this, "hass-api-called", eventdata);
|
||||
} catch (err: any) {
|
||||
} catch (err) {
|
||||
showAlertDialog(this, {
|
||||
title: this.supervisor.localize(
|
||||
"addon.dashboard.action_error.uninstall"
|
||||
@@ -1152,7 +1140,6 @@ class HassioAddonInfo extends LitElement {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
img.logo {
|
||||
max-width: 100%;
|
||||
max-height: 60px;
|
||||
margin: 16px 0;
|
||||
display: block;
|
||||
@@ -1162,10 +1149,10 @@ class HassioAddonInfo extends LitElement {
|
||||
display: flex;
|
||||
}
|
||||
ha-svg-icon.running {
|
||||
color: var(--success-color);
|
||||
color: var(--paper-green-400);
|
||||
}
|
||||
ha-svg-icon.stopped {
|
||||
color: var(--error-color);
|
||||
color: var(--google-red-300);
|
||||
}
|
||||
ha-call-api-button {
|
||||
font-weight: 500;
|
||||
|
@@ -1,7 +1,6 @@
|
||||
import "@material/mwc-button";
|
||||
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import "../../../../src/components/ha-alert";
|
||||
import "../../../../src/components/ha-card";
|
||||
import {
|
||||
fetchHassioAddonLogs,
|
||||
@@ -35,9 +34,7 @@ class HassioAddonLogs extends LitElement {
|
||||
return html`
|
||||
<h1>${this.addon.name}</h1>
|
||||
<ha-card>
|
||||
${this._error
|
||||
? html`<ha-alert alert-type="error">${this._error}</ha-alert>`
|
||||
: ""}
|
||||
${this._error ? html` <div class="errors">${this._error}</div> ` : ""}
|
||||
<div class="card-content">
|
||||
${this._content
|
||||
? html`<hassio-ansi-to-html
|
||||
@@ -63,6 +60,10 @@ class HassioAddonLogs extends LitElement {
|
||||
ha-card {
|
||||
display: block;
|
||||
}
|
||||
.errors {
|
||||
color: var(--error-color);
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
`,
|
||||
];
|
||||
}
|
||||
@@ -71,7 +72,7 @@ class HassioAddonLogs extends LitElement {
|
||||
this._error = undefined;
|
||||
try {
|
||||
this._content = await fetchHassioAddonLogs(this.hass, this.addon.slug);
|
||||
} catch (err: any) {
|
||||
} catch (err) {
|
||||
this._error = this.supervisor.localize(
|
||||
"addon.logs.get_logs",
|
||||
"error",
|
||||
|
@@ -41,16 +41,16 @@ class HassioAnsiToHtml extends LitElement {
|
||||
text-decoration: underline line-through;
|
||||
}
|
||||
.fg-red {
|
||||
color: var(--error-color);
|
||||
color: rgb(222, 56, 43);
|
||||
}
|
||||
.fg-green {
|
||||
color: var(--success-color);
|
||||
color: rgb(57, 181, 74);
|
||||
}
|
||||
.fg-yellow {
|
||||
color: var(--warning-color);
|
||||
color: rgb(255, 199, 6);
|
||||
}
|
||||
.fg-blue {
|
||||
color: var(--info-color);
|
||||
color: rgb(0, 111, 184);
|
||||
}
|
||||
.fg-magenta {
|
||||
color: rgb(118, 38, 113);
|
||||
@@ -65,16 +65,16 @@ class HassioAnsiToHtml extends LitElement {
|
||||
background-color: rgb(0, 0, 0);
|
||||
}
|
||||
.bg-red {
|
||||
background-color: var(--error-color);
|
||||
background-color: rgb(222, 56, 43);
|
||||
}
|
||||
.bg-green {
|
||||
background-color: var(--success-color);
|
||||
background-color: rgb(57, 181, 74);
|
||||
}
|
||||
.bg-yellow {
|
||||
background-color: var(--warning-color);
|
||||
background-color: rgb(255, 199, 6);
|
||||
}
|
||||
.bg-blue {
|
||||
background-color: var(--info-color);
|
||||
background-color: rgb(0, 111, 184);
|
||||
}
|
||||
.bg-magenta {
|
||||
background-color: rgb(118, 38, 113);
|
||||
|
@@ -37,7 +37,7 @@ class HassioCardContent extends LitElement {
|
||||
${this.iconImage
|
||||
? html`
|
||||
<div class="icon_image ${this.iconClass}">
|
||||
<img src=${this.iconImage} .title=${this.iconTitle} />
|
||||
<img src="${this.iconImage}" .title=${this.iconTitle} />
|
||||
<div></div>
|
||||
</div>
|
||||
`
|
||||
@@ -80,14 +80,14 @@ class HassioCardContent extends LitElement {
|
||||
color: var(--secondary-text-color);
|
||||
}
|
||||
ha-svg-icon.update {
|
||||
color: var(--warning-color);
|
||||
color: var(--paper-orange-400);
|
||||
}
|
||||
ha-svg-icon.running,
|
||||
ha-svg-icon.installed {
|
||||
color: var(--success-color);
|
||||
color: var(--paper-green-400);
|
||||
}
|
||||
ha-svg-icon.hassupdate,
|
||||
ha-svg-icon.backup {
|
||||
ha-svg-icon.snapshot {
|
||||
color: var(--paper-item-icon-color);
|
||||
}
|
||||
ha-svg-icon.not_available {
|
||||
@@ -122,7 +122,7 @@ class HassioCardContent extends LitElement {
|
||||
}
|
||||
.dot {
|
||||
position: absolute;
|
||||
background-color: var(--warning-color);
|
||||
background-color: var(--paper-orange-400);
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
top: 8px;
|
||||
|
@@ -1,5 +1,6 @@
|
||||
import "@material/mwc-icon-button/mwc-icon-button";
|
||||
import { mdiFolderUpload } from "@mdi/js";
|
||||
import "@polymer/iron-input/iron-input";
|
||||
import "@polymer/paper-input/paper-input-container";
|
||||
import { html, LitElement, TemplateResult } from "lit";
|
||||
import { customElement, state } from "lit/decorators";
|
||||
@@ -8,20 +9,23 @@ import "../../../src/components/ha-circular-progress";
|
||||
import "../../../src/components/ha-file-upload";
|
||||
import "../../../src/components/ha-svg-icon";
|
||||
import { extractApiErrorMessage } from "../../../src/data/hassio/common";
|
||||
import { HassioBackup, uploadBackup } from "../../../src/data/hassio/backup";
|
||||
import {
|
||||
HassioSnapshot,
|
||||
uploadSnapshot,
|
||||
} from "../../../src/data/hassio/snapshot";
|
||||
import { showAlertDialog } from "../../../src/dialogs/generic/show-dialog-box";
|
||||
import { HomeAssistant } from "../../../src/types";
|
||||
|
||||
declare global {
|
||||
interface HASSDomEvents {
|
||||
"backup-uploaded": { backup: HassioBackup };
|
||||
"snapshot-uploaded": { snapshot: HassioSnapshot };
|
||||
}
|
||||
}
|
||||
|
||||
const MAX_FILE_SIZE = 1 * 1024 * 1024 * 1024; // 1GB
|
||||
|
||||
@customElement("hassio-upload-backup")
|
||||
export class HassioUploadBackup extends LitElement {
|
||||
@customElement("hassio-upload-snapshot")
|
||||
export class HassioUploadSnapshot extends LitElement {
|
||||
public hass!: HomeAssistant;
|
||||
|
||||
@state() public value: string | null = null;
|
||||
@@ -34,7 +38,7 @@ export class HassioUploadBackup extends LitElement {
|
||||
.uploading=${this._uploading}
|
||||
.icon=${mdiFolderUpload}
|
||||
accept="application/x-tar"
|
||||
label="Upload backup"
|
||||
label="Upload snapshot"
|
||||
@file-picked=${this._uploadFile}
|
||||
auto-open-file-dialog
|
||||
></ha-file-upload>
|
||||
@@ -46,10 +50,10 @@ export class HassioUploadBackup extends LitElement {
|
||||
|
||||
if (file.size > MAX_FILE_SIZE) {
|
||||
showAlertDialog(this, {
|
||||
title: "Backup file is too big",
|
||||
title: "Snapshot file is too big",
|
||||
text: html`The maximum allowed filesize is 1GB.<br />
|
||||
<a
|
||||
href="https://www.home-assistant.io/hassio/haos_common_tasks/#restoring-a-backup-on-a-new-install"
|
||||
href="https://www.home-assistant.io/hassio/haos_common_tasks/#restoring-a-snapshot-on-a-new-install"
|
||||
target="_blank"
|
||||
>Have a look here on how to restore it.</a
|
||||
>`,
|
||||
@@ -61,16 +65,16 @@ export class HassioUploadBackup extends LitElement {
|
||||
if (!["application/x-tar"].includes(file.type)) {
|
||||
showAlertDialog(this, {
|
||||
title: "Unsupported file format",
|
||||
text: "Please choose a Home Assistant backup file (.tar)",
|
||||
text: "Please choose a Home Assistant snapshot file (.tar)",
|
||||
confirmText: "ok",
|
||||
});
|
||||
return;
|
||||
}
|
||||
this._uploading = true;
|
||||
try {
|
||||
const backup = await uploadBackup(this.hass, file);
|
||||
fireEvent(this, "backup-uploaded", { backup: backup.data });
|
||||
} catch (err: any) {
|
||||
const snapshot = await uploadSnapshot(this.hass, file);
|
||||
fireEvent(this, "snapshot-uploaded", { snapshot: snapshot.data });
|
||||
} catch (err) {
|
||||
showAlertDialog(this, {
|
||||
title: "Upload failed",
|
||||
text: extractApiErrorMessage(err),
|
||||
@@ -84,6 +88,6 @@ export class HassioUploadBackup extends LitElement {
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"hassio-upload-backup": HassioUploadBackup;
|
||||
"hassio-upload-snapshot": HassioUploadSnapshot;
|
||||
}
|
||||
}
|
@@ -29,6 +29,7 @@ class SupervisorFormfieldLabel extends LitElement {
|
||||
static get styles(): CSSResultGroup {
|
||||
return css`
|
||||
:host {
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
@@ -20,10 +20,10 @@ class SupervisorMetric extends LitElement {
|
||||
<div slot="description" .title=${this.tooltip ?? ""}>
|
||||
<span class="value"> ${roundedValue} % </span>
|
||||
<ha-bar
|
||||
class=${classMap({
|
||||
class="${classMap({
|
||||
"target-warning": roundedValue > 50,
|
||||
"target-critical": roundedValue > 85,
|
||||
})}
|
||||
})}"
|
||||
.value=${this.value}
|
||||
></ha-bar>
|
||||
</div>
|
||||
|
@@ -5,16 +5,15 @@ import { customElement, property } from "lit/decorators";
|
||||
import { atLeastVersion } from "../../../src/common/config/version";
|
||||
import { formatDate } from "../../../src/common/datetime/format_date";
|
||||
import { formatDateTime } from "../../../src/common/datetime/format_date_time";
|
||||
import { LocalizeFunc } from "../../../src/common/translations/localize";
|
||||
import "../../../src/components/ha-checkbox";
|
||||
import "../../../src/components/ha-formfield";
|
||||
import "../../../src/components/ha-radio";
|
||||
import type { HaRadio } from "../../../src/components/ha-radio";
|
||||
import {
|
||||
HassioFullBackupCreateParams,
|
||||
HassioPartialBackupCreateParams,
|
||||
HassioBackupDetail,
|
||||
} from "../../../src/data/hassio/backup";
|
||||
HassioFullSnapshotCreateParams,
|
||||
HassioPartialSnapshotCreateParams,
|
||||
HassioSnapshotDetail,
|
||||
} from "../../../src/data/hassio/snapshot";
|
||||
import { Supervisor } from "../../../src/data/supervisor/supervisor";
|
||||
import { PolymerChangedEvent } from "../../../src/polymer-types";
|
||||
import { HomeAssistant } from "../../../src/types";
|
||||
@@ -45,9 +44,6 @@ const _computeFolders = (folders): CheckboxItem[] => {
|
||||
if (folders.includes("share")) {
|
||||
list.push({ slug: "share", name: "Share", checked: false });
|
||||
}
|
||||
if (folders.includes("media")) {
|
||||
list.push({ slug: "media", name: "Media", checked: false });
|
||||
}
|
||||
if (folders.includes("addons/local")) {
|
||||
list.push({ slug: "addons/local", name: "Local add-ons", checked: false });
|
||||
}
|
||||
@@ -64,17 +60,15 @@ const _computeAddons = (addons): AddonCheckboxItem[] =>
|
||||
}))
|
||||
.sort((a, b) => (a.name > b.name ? 1 : -1));
|
||||
|
||||
@customElement("supervisor-backup-content")
|
||||
export class SupervisorBackupContent extends LitElement {
|
||||
@customElement("supervisor-snapshot-content")
|
||||
export class SupervisorSnapshotContent extends LitElement {
|
||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||
|
||||
@property() public localize?: LocalizeFunc;
|
||||
|
||||
@property({ attribute: false }) public supervisor?: Supervisor;
|
||||
|
||||
@property({ attribute: false }) public backup?: HassioBackupDetail;
|
||||
@property({ attribute: false }) public snapshot?: HassioSnapshotDetail;
|
||||
|
||||
@property() public backupType: HassioBackupDetail["type"] = "full";
|
||||
@property() public snapshotType: HassioSnapshotDetail["type"] = "full";
|
||||
|
||||
@property({ attribute: false }) public folders?: CheckboxItem[];
|
||||
|
||||
@@ -82,116 +76,118 @@ export class SupervisorBackupContent extends LitElement {
|
||||
|
||||
@property({ type: Boolean }) public homeAssistant = false;
|
||||
|
||||
@property({ type: Boolean }) public backupHasPassword = false;
|
||||
@property({ type: Boolean }) public snapshotHasPassword = false;
|
||||
|
||||
@property({ type: Boolean }) public onboarding = false;
|
||||
@property() public snapshotName = "";
|
||||
|
||||
@property() public backupName = "";
|
||||
|
||||
@property() public backupPassword = "";
|
||||
|
||||
@property() public confirmBackupPassword = "";
|
||||
@property() public snapshotPassword = "";
|
||||
|
||||
public willUpdate(changedProps) {
|
||||
super.willUpdate(changedProps);
|
||||
if (!this.hasUpdated) {
|
||||
this.folders = _computeFolders(
|
||||
this.backup
|
||||
? this.backup.folders
|
||||
this.snapshot
|
||||
? this.snapshot.folders
|
||||
: ["homeassistant", "ssl", "share", "media", "addons/local"]
|
||||
);
|
||||
this.addons = _computeAddons(
|
||||
this.backup ? this.backup.addons : this.supervisor?.supervisor.addons
|
||||
this.snapshot
|
||||
? this.snapshot.addons
|
||||
: this.supervisor?.supervisor.addons
|
||||
);
|
||||
this.backupType = this.backup?.type || "full";
|
||||
this.backupName = this.backup?.name || "";
|
||||
this.backupHasPassword = this.backup?.protected || false;
|
||||
this.snapshotType = this.snapshot?.type || "full";
|
||||
this.snapshotName = this.snapshot?.name || "";
|
||||
this.snapshotHasPassword = this.snapshot?.protected || false;
|
||||
}
|
||||
}
|
||||
|
||||
private _localize = (string: string) =>
|
||||
this.supervisor?.localize(`backup.${string}`) ||
|
||||
this.localize!(`ui.panel.page-onboarding.restore.${string}`);
|
||||
|
||||
protected render(): TemplateResult {
|
||||
if (!this.onboarding && !this.supervisor) {
|
||||
if (!this.supervisor) {
|
||||
return html``;
|
||||
}
|
||||
const foldersSection =
|
||||
this.backupType === "partial" ? this._getSection("folders") : undefined;
|
||||
this.snapshotType === "partial" ? this._getSection("folders") : undefined;
|
||||
const addonsSection =
|
||||
this.backupType === "partial" ? this._getSection("addons") : undefined;
|
||||
this.snapshotType === "partial" ? this._getSection("addons") : undefined;
|
||||
|
||||
return html`
|
||||
${this.backup
|
||||
${this.snapshot
|
||||
? html`<div class="details">
|
||||
${this.backup.type === "full"
|
||||
? this._localize("full_backup")
|
||||
: this._localize("partial_backup")}
|
||||
(${Math.ceil(this.backup.size * 10) / 10 + " MB"})<br />
|
||||
${this.hass
|
||||
? formatDateTime(new Date(this.backup.date), this.hass.locale)
|
||||
: this.backup.date}
|
||||
${this.snapshot.type === "full"
|
||||
? this.supervisor.localize("snapshot.full_snapshot")
|
||||
: this.supervisor.localize("snapshot.partial_snapshot")}
|
||||
(${Math.ceil(this.snapshot.size * 10) / 10 + " MB"})<br />
|
||||
${formatDateTime(new Date(this.snapshot.date), this.hass.locale)}
|
||||
</div>`
|
||||
: html`<paper-input
|
||||
name="backupName"
|
||||
.label=${this._localize("name")}
|
||||
.value=${this.backupName}
|
||||
name="snapshotName"
|
||||
.label=${this.supervisor.localize("snapshot.name")}
|
||||
.value=${this.snapshotName}
|
||||
@value-changed=${this._handleTextValueChanged}
|
||||
>
|
||||
</paper-input>`}
|
||||
${!this.backup || this.backup.type === "full"
|
||||
${!this.snapshot || this.snapshot.type === "full"
|
||||
? html`<div class="sub-header">
|
||||
${!this.backup
|
||||
? this._localize("type")
|
||||
: this._localize("select_type")}
|
||||
${!this.snapshot
|
||||
? this.supervisor.localize("snapshot.type")
|
||||
: this.supervisor.localize("snapshot.select_type")}
|
||||
</div>
|
||||
<div class="backup-types">
|
||||
<ha-formfield .label=${this._localize("full_backup")}>
|
||||
<div class="snapshot-types">
|
||||
<ha-formfield
|
||||
.label=${this.supervisor.localize("snapshot.full_snapshot")}
|
||||
>
|
||||
<ha-radio
|
||||
@change=${this._handleRadioValueChanged}
|
||||
value="full"
|
||||
name="backupType"
|
||||
.checked=${this.backupType === "full"}
|
||||
name="snapshotType"
|
||||
.checked=${this.snapshotType === "full"}
|
||||
>
|
||||
</ha-radio>
|
||||
</ha-formfield>
|
||||
<ha-formfield .label=${this._localize("partial_backup")}>
|
||||
<ha-formfield
|
||||
.label=${this.supervisor!.localize("snapshot.partial_snapshot")}
|
||||
>
|
||||
<ha-radio
|
||||
@change=${this._handleRadioValueChanged}
|
||||
value="partial"
|
||||
name="backupType"
|
||||
.checked=${this.backupType === "partial"}
|
||||
name="snapshotType"
|
||||
.checked=${this.snapshotType === "partial"}
|
||||
>
|
||||
</ha-radio>
|
||||
</ha-formfield>
|
||||
</div>`
|
||||
: ""}
|
||||
${this.backupType === "partial"
|
||||
? html`<div class="partial-picker">
|
||||
${this.backup && this.backup.homeassistant
|
||||
${this.snapshot && this.snapshotType === "partial"
|
||||
? html`
|
||||
${this.snapshot.homeassistant
|
||||
? html`
|
||||
<ha-formfield
|
||||
.label=${html`<supervisor-formfield-label
|
||||
label="Home Assistant"
|
||||
.iconPath=${mdiHomeAssistant}
|
||||
.version=${this.backup.homeassistant}
|
||||
.version=${this.snapshot.homeassistant}
|
||||
>
|
||||
</supervisor-formfield-label>`}
|
||||
>
|
||||
<ha-checkbox
|
||||
.checked=${this.homeAssistant}
|
||||
@click=${this.toggleHomeAssistant}
|
||||
@click=${() => {
|
||||
this.homeAssistant = !this.homeAssistant;
|
||||
}}
|
||||
>
|
||||
</ha-checkbox>
|
||||
</ha-formfield>
|
||||
`
|
||||
: ""}
|
||||
`
|
||||
: ""}
|
||||
${this.snapshotType === "partial"
|
||||
? html`
|
||||
${foldersSection?.templates.length
|
||||
? html`
|
||||
<ha-formfield
|
||||
.label=${html`<supervisor-formfield-label
|
||||
.label=${this._localize("folders")}
|
||||
.label=${this.supervisor.localize("snapshot.folders")}
|
||||
.iconPath=${mdiFolder}
|
||||
>
|
||||
</supervisor-formfield-label>`}
|
||||
@@ -211,7 +207,7 @@ export class SupervisorBackupContent extends LitElement {
|
||||
? html`
|
||||
<ha-formfield
|
||||
.label=${html`<supervisor-formfield-label
|
||||
.label=${this._localize("addons")}
|
||||
.label=${this.supervisor.localize("snapshot.addons")}
|
||||
.iconPath=${mdiPuzzle}
|
||||
>
|
||||
</supervisor-formfield-label>`}
|
||||
@@ -227,73 +223,51 @@ export class SupervisorBackupContent extends LitElement {
|
||||
<div class="section-content">${addonsSection.templates}</div>
|
||||
`
|
||||
: ""}
|
||||
</div> `
|
||||
`
|
||||
: ""}
|
||||
${this.backupType === "partial" &&
|
||||
(!this.backup || this.backupHasPassword)
|
||||
? html`<hr />`
|
||||
: ""}
|
||||
${!this.backup
|
||||
${!this.snapshot
|
||||
? html`<ha-formfield
|
||||
class="password"
|
||||
.label=${this._localize("password_protection")}
|
||||
.label=${this.supervisor.localize("snapshot.password_protection")}
|
||||
>
|
||||
<ha-checkbox
|
||||
.checked=${this.backupHasPassword}
|
||||
.checked=${this.snapshotHasPassword}
|
||||
@change=${this._toggleHasPassword}
|
||||
>
|
||||
</ha-checkbox>
|
||||
</ha-formfield>`
|
||||
</ha-checkbox
|
||||
></ha-formfield>`
|
||||
: ""}
|
||||
${this.backupHasPassword
|
||||
${this.snapshotHasPassword
|
||||
? html`
|
||||
<paper-input
|
||||
.label=${this._localize("password")}
|
||||
.label=${this.supervisor.localize("snapshot.password")}
|
||||
type="password"
|
||||
name="backupPassword"
|
||||
.value=${this.backupPassword}
|
||||
name="snapshotPassword"
|
||||
.value=${this.snapshotPassword}
|
||||
@value-changed=${this._handleTextValueChanged}
|
||||
>
|
||||
</paper-input>
|
||||
${!this.backup
|
||||
? html` <paper-input
|
||||
.label=${this._localize("confirm_password")}
|
||||
type="password"
|
||||
name="confirmBackupPassword"
|
||||
.value=${this.confirmBackupPassword}
|
||||
@value-changed=${this._handleTextValueChanged}
|
||||
>
|
||||
</paper-input>`
|
||||
: ""}
|
||||
`
|
||||
: ""}
|
||||
`;
|
||||
}
|
||||
|
||||
private toggleHomeAssistant() {
|
||||
this.homeAssistant = !this.homeAssistant;
|
||||
}
|
||||
|
||||
static get styles(): CSSResultGroup {
|
||||
return css`
|
||||
.partial-picker ha-formfield {
|
||||
ha-checkbox {
|
||||
--mdc-checkbox-touch-target-size: 16px;
|
||||
display: block;
|
||||
margin: 4px 12px 8px 0;
|
||||
}
|
||||
.partial-picker ha-checkbox {
|
||||
--mdc-checkbox-touch-target-size: 32px;
|
||||
}
|
||||
.partial-picker {
|
||||
display: block;
|
||||
margin: 0px -6px;
|
||||
ha-formfield {
|
||||
display: contents;
|
||||
}
|
||||
supervisor-formfield-label {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
}
|
||||
hr {
|
||||
border-color: var(--divider-color);
|
||||
border-bottom: none;
|
||||
margin: 16px 0;
|
||||
paper-input[type="password"] {
|
||||
display: block;
|
||||
margin: 4px 0 4px 16px;
|
||||
}
|
||||
.details {
|
||||
color: var(--secondary-text-color);
|
||||
@@ -301,15 +275,13 @@ export class SupervisorBackupContent extends LitElement {
|
||||
.section-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
margin-left: 30px;
|
||||
margin-left: 16px;
|
||||
}
|
||||
ha-formfield.password {
|
||||
display: block;
|
||||
margin: 0 -14px -16px;
|
||||
.security {
|
||||
margin-top: 16px;
|
||||
}
|
||||
.backup-types {
|
||||
.snapshot-types {
|
||||
display: flex;
|
||||
margin-left: -13px;
|
||||
}
|
||||
.sub-header {
|
||||
margin-top: 8px;
|
||||
@@ -317,23 +289,20 @@ export class SupervisorBackupContent extends LitElement {
|
||||
`;
|
||||
}
|
||||
|
||||
public backupDetails():
|
||||
| HassioPartialBackupCreateParams
|
||||
| HassioFullBackupCreateParams {
|
||||
public snapshotDetails():
|
||||
| HassioPartialSnapshotCreateParams
|
||||
| HassioFullSnapshotCreateParams {
|
||||
const data: any = {};
|
||||
|
||||
if (!this.backup) {
|
||||
data.name = this.backupName || formatDate(new Date(), this.hass.locale);
|
||||
if (!this.snapshot) {
|
||||
data.name = this.snapshotName || formatDate(new Date(), this.hass.locale);
|
||||
}
|
||||
|
||||
if (this.backupHasPassword) {
|
||||
data.password = this.backupPassword;
|
||||
if (!this.backup) {
|
||||
data.confirm_password = this.confirmBackupPassword;
|
||||
}
|
||||
if (this.snapshotHasPassword) {
|
||||
data.password = this.snapshotPassword;
|
||||
}
|
||||
|
||||
if (this.backupType === "full") {
|
||||
if (this.snapshotType === "full") {
|
||||
return data;
|
||||
}
|
||||
|
||||
@@ -362,7 +331,7 @@ export class SupervisorBackupContent extends LitElement {
|
||||
const addons =
|
||||
section === "addons"
|
||||
? new Map(
|
||||
this.supervisor?.addon.addons.map((item) => [item.slug, item])
|
||||
this.supervisor!.addon.addons.map((item) => [item.slug, item])
|
||||
)
|
||||
: undefined;
|
||||
let checkedItems = 0;
|
||||
@@ -372,7 +341,6 @@ export class SupervisorBackupContent extends LitElement {
|
||||
.label=${item.name}
|
||||
.iconPath=${section === "addons" ? mdiPuzzle : mdiFolder}
|
||||
.imageUrl=${section === "addons" &&
|
||||
!this.onboarding &&
|
||||
atLeastVersion(this.hass.config.version, 0, 105) &&
|
||||
addons?.get(item.slug)?.icon
|
||||
? `/api/hassio/addons/${item.slug}/icon`
|
||||
@@ -415,7 +383,7 @@ export class SupervisorBackupContent extends LitElement {
|
||||
}
|
||||
|
||||
private _toggleHasPassword(): void {
|
||||
this.backupHasPassword = !this.backupHasPassword;
|
||||
this.snapshotHasPassword = !this.snapshotHasPassword;
|
||||
}
|
||||
|
||||
private _toggleSection(ev): void {
|
||||
@@ -445,6 +413,6 @@ export class SupervisorBackupContent extends LitElement {
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"supervisor-backup-content": SupervisorBackupContent;
|
||||
"supervisor-snapshot-content": SupervisorSnapshotContent;
|
||||
}
|
||||
}
|
@@ -3,7 +3,7 @@ import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
||||
import { customElement, property } from "lit/decorators";
|
||||
import { atLeastVersion } from "../../../src/common/config/version";
|
||||
import { navigate } from "../../../src/common/navigate";
|
||||
import { stringCompare } from "../../../src/common/string/compare";
|
||||
import { compare } from "../../../src/common/string/compare";
|
||||
import "../../../src/components/ha-card";
|
||||
import { Supervisor } from "../../../src/data/supervisor/supervisor";
|
||||
import { haStyle } from "../../../src/resources/styles";
|
||||
@@ -33,7 +33,7 @@ class HassioAddons extends LitElement {
|
||||
</ha-card>
|
||||
`
|
||||
: this.supervisor.supervisor.addons
|
||||
.sort((a, b) => stringCompare(a.name, b.name))
|
||||
.sort((a, b) => compare(a.name, b.name))
|
||||
.map(
|
||||
(addon) => html`
|
||||
<ha-card .addon=${addon} @click=${this._addonTapped}>
|
||||
|
@@ -86,7 +86,7 @@ export class HassioUpdate extends LitElement {
|
||||
"hassio/supervisor/update",
|
||||
`https://github.com//home-assistant/hassio/releases/tag/${this.supervisor.supervisor.version_latest}`
|
||||
)}
|
||||
${this.supervisor.host.features.includes("haos")
|
||||
${this.supervisor.host.features.includes("hassos")
|
||||
? this._renderUpdateCard(
|
||||
"Operating System",
|
||||
"os",
|
||||
@@ -136,7 +136,7 @@ export class HassioUpdate extends LitElement {
|
||||
</ha-settings-row>
|
||||
</div>
|
||||
<div class="card-actions">
|
||||
<a href=${releaseNotesUrl} target="_blank" rel="noreferrer">
|
||||
<a href="${releaseNotesUrl}" target="_blank" rel="noreferrer">
|
||||
<mwc-button>
|
||||
${this.supervisor.localize("common.release_notes")}
|
||||
</mwc-button>
|
||||
@@ -162,7 +162,7 @@ export class HassioUpdate extends LitElement {
|
||||
supervisor: this.supervisor,
|
||||
name: "Home Assistant Core",
|
||||
version: this.supervisor.core.version_latest,
|
||||
backupParams: {
|
||||
snapshotParams: {
|
||||
name: `core_${this.supervisor.core.version}`,
|
||||
folders: ["homeassistant"],
|
||||
homeassistant: true,
|
||||
@@ -206,7 +206,7 @@ export class HassioUpdate extends LitElement {
|
||||
fireEvent(this, "supervisor-collection-refresh", {
|
||||
collection: item.key,
|
||||
});
|
||||
} catch (err: any) {
|
||||
} catch (err) {
|
||||
// Only show an error if the status code was not expected (user behind proxy)
|
||||
// or no status at all(connection terminated)
|
||||
if (this.hass.connection.connected && !ignoreSupervisorError(err)) {
|
||||
|
@@ -1,352 +0,0 @@
|
||||
import { ActionDetail } from "@material/mwc-list";
|
||||
import "@material/mwc-list/mwc-list-item";
|
||||
import { mdiClose, mdiDotsVertical } from "@mdi/js";
|
||||
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
||||
import { customElement, property, query, state } from "lit/decorators";
|
||||
import { fireEvent } from "../../../../src/common/dom/fire_event";
|
||||
import { slugify } from "../../../../src/common/string/slugify";
|
||||
import "../../../../src/components/buttons/ha-progress-button";
|
||||
import "../../../../src/components/ha-alert";
|
||||
import "../../../../src/components/ha-button-menu";
|
||||
import "../../../../src/components/ha-header-bar";
|
||||
import "../../../../src/components/ha-svg-icon";
|
||||
import { getSignedPath } from "../../../../src/data/auth";
|
||||
import { extractApiErrorMessage } from "../../../../src/data/hassio/common";
|
||||
import {
|
||||
fetchHassioBackupInfo,
|
||||
HassioBackupDetail,
|
||||
} from "../../../../src/data/hassio/backup";
|
||||
import {
|
||||
showAlertDialog,
|
||||
showConfirmationDialog,
|
||||
} from "../../../../src/dialogs/generic/show-dialog-box";
|
||||
import { HassDialog } from "../../../../src/dialogs/make-dialog-manager";
|
||||
import { haStyle, haStyleDialog } from "../../../../src/resources/styles";
|
||||
import { HomeAssistant } from "../../../../src/types";
|
||||
import { fileDownload } from "../../../../src/util/file_download";
|
||||
import "../../components/supervisor-backup-content";
|
||||
import type { SupervisorBackupContent } from "../../components/supervisor-backup-content";
|
||||
import { HassioBackupDialogParams } from "./show-dialog-hassio-backup";
|
||||
import { atLeastVersion } from "../../../../src/common/config/version";
|
||||
import { stopPropagation } from "../../../../src/common/dom/stop_propagation";
|
||||
|
||||
@customElement("dialog-hassio-backup")
|
||||
class HassioBackupDialog
|
||||
extends LitElement
|
||||
implements HassDialog<HassioBackupDialogParams>
|
||||
{
|
||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||
|
||||
@state() private _error?: string;
|
||||
|
||||
@state() private _backup?: HassioBackupDetail;
|
||||
|
||||
@state() private _dialogParams?: HassioBackupDialogParams;
|
||||
|
||||
@state() private _restoringBackup = false;
|
||||
|
||||
@query("supervisor-backup-content")
|
||||
private _backupContent!: SupervisorBackupContent;
|
||||
|
||||
public async showDialog(params: HassioBackupDialogParams) {
|
||||
this._backup = await fetchHassioBackupInfo(this.hass, params.slug);
|
||||
this._dialogParams = params;
|
||||
this._restoringBackup = false;
|
||||
}
|
||||
|
||||
public closeDialog() {
|
||||
this._backup = undefined;
|
||||
this._dialogParams = undefined;
|
||||
this._restoringBackup = false;
|
||||
this._error = undefined;
|
||||
fireEvent(this, "dialog-closed", { dialog: this.localName });
|
||||
}
|
||||
|
||||
protected render(): TemplateResult {
|
||||
if (!this._dialogParams || !this._backup) {
|
||||
return html``;
|
||||
}
|
||||
return html`
|
||||
<ha-dialog
|
||||
open
|
||||
scrimClickAction
|
||||
@closed=${this.closeDialog}
|
||||
.heading=${true}
|
||||
>
|
||||
<div slot="heading">
|
||||
<ha-header-bar>
|
||||
<span slot="title">${this._backup.name}</span>
|
||||
<mwc-icon-button slot="actionItems" dialogAction="cancel">
|
||||
<ha-svg-icon .path=${mdiClose}></ha-svg-icon>
|
||||
</mwc-icon-button>
|
||||
</ha-header-bar>
|
||||
</div>
|
||||
${this._restoringBackup
|
||||
? html` <ha-circular-progress active></ha-circular-progress>`
|
||||
: html`<supervisor-backup-content
|
||||
.hass=${this.hass}
|
||||
.supervisor=${this._dialogParams.supervisor}
|
||||
.backup=${this._backup}
|
||||
.onboarding=${this._dialogParams.onboarding || false}
|
||||
.localize=${this._dialogParams.localize}
|
||||
>
|
||||
</supervisor-backup-content>`}
|
||||
${this._error
|
||||
? html`<ha-alert alert-type="error">${this._error}</ha-alert>`
|
||||
: ""}
|
||||
|
||||
<mwc-button
|
||||
.disabled=${this._restoringBackup}
|
||||
slot="secondaryAction"
|
||||
@click=${this._restoreClicked}
|
||||
>
|
||||
Restore
|
||||
</mwc-button>
|
||||
|
||||
${!this._dialogParams.onboarding
|
||||
? html`<ha-button-menu
|
||||
fixed
|
||||
slot="primaryAction"
|
||||
@action=${this._handleMenuAction}
|
||||
@closed=${stopPropagation}
|
||||
>
|
||||
<mwc-icon-button slot="trigger" alt="menu">
|
||||
<ha-svg-icon .path=${mdiDotsVertical}></ha-svg-icon>
|
||||
</mwc-icon-button>
|
||||
<mwc-list-item>Download Backup</mwc-list-item>
|
||||
<mwc-list-item class="error">Delete Backup</mwc-list-item>
|
||||
</ha-button-menu>`
|
||||
: ""}
|
||||
</ha-dialog>
|
||||
`;
|
||||
}
|
||||
|
||||
static get styles(): CSSResultGroup {
|
||||
return [
|
||||
haStyle,
|
||||
haStyleDialog,
|
||||
css`
|
||||
ha-svg-icon {
|
||||
color: var(--primary-text-color);
|
||||
}
|
||||
ha-circular-progress {
|
||||
display: block;
|
||||
text-align: center;
|
||||
}
|
||||
ha-header-bar {
|
||||
--mdc-theme-on-primary: var(--primary-text-color);
|
||||
--mdc-theme-primary: var(--mdc-theme-surface);
|
||||
flex-shrink: 0;
|
||||
display: block;
|
||||
}
|
||||
`,
|
||||
];
|
||||
}
|
||||
|
||||
private _handleMenuAction(ev: CustomEvent<ActionDetail>) {
|
||||
switch (ev.detail.index) {
|
||||
case 0:
|
||||
this._downloadClicked();
|
||||
break;
|
||||
case 1:
|
||||
this._deleteClicked();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private async _restoreClicked() {
|
||||
const backupDetails = this._backupContent.backupDetails();
|
||||
this._restoringBackup = true;
|
||||
if (this._backupContent.backupType === "full") {
|
||||
await this._fullRestoreClicked(backupDetails);
|
||||
} else {
|
||||
await this._partialRestoreClicked(backupDetails);
|
||||
}
|
||||
this._restoringBackup = false;
|
||||
}
|
||||
|
||||
private async _partialRestoreClicked(backupDetails) {
|
||||
if (
|
||||
this._dialogParams?.supervisor !== undefined &&
|
||||
this._dialogParams?.supervisor.info.state !== "running"
|
||||
) {
|
||||
await showAlertDialog(this, {
|
||||
title: "Could not restore backup",
|
||||
text: `Restoring a backup is not possible right now because the system is in ${this._dialogParams?.supervisor.info.state} state.`,
|
||||
});
|
||||
return;
|
||||
}
|
||||
if (
|
||||
!(await showConfirmationDialog(this, {
|
||||
title: "Are you sure you want partially to restore this backup?",
|
||||
confirmText: "restore",
|
||||
dismissText: "cancel",
|
||||
}))
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this._dialogParams?.onboarding) {
|
||||
this.hass
|
||||
.callApi(
|
||||
"POST",
|
||||
|
||||
`hassio/${
|
||||
atLeastVersion(this.hass.config.version, 2021, 9)
|
||||
? "backups"
|
||||
: "snapshots"
|
||||
}/${this._backup!.slug}/restore/partial`,
|
||||
backupDetails
|
||||
)
|
||||
.then(
|
||||
() => {
|
||||
this.closeDialog();
|
||||
},
|
||||
(error) => {
|
||||
this._error = error.body.message;
|
||||
}
|
||||
);
|
||||
} else {
|
||||
fireEvent(this, "restoring");
|
||||
fetch(`/api/hassio/backups/${this._backup!.slug}/restore/partial`, {
|
||||
method: "POST",
|
||||
body: JSON.stringify(backupDetails),
|
||||
});
|
||||
this.closeDialog();
|
||||
}
|
||||
}
|
||||
|
||||
private async _fullRestoreClicked(backupDetails) {
|
||||
if (
|
||||
this._dialogParams?.supervisor !== undefined &&
|
||||
this._dialogParams?.supervisor.info.state !== "running"
|
||||
) {
|
||||
await showAlertDialog(this, {
|
||||
title: "Could not restore backup",
|
||||
text: `Restoring a backup is not possible right now because the system is in ${this._dialogParams?.supervisor.info.state} state.`,
|
||||
});
|
||||
return;
|
||||
}
|
||||
if (
|
||||
!(await showConfirmationDialog(this, {
|
||||
title:
|
||||
"Are you sure you want to wipe your system and restore this backup?",
|
||||
confirmText: "restore",
|
||||
dismissText: "cancel",
|
||||
}))
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this._dialogParams?.onboarding) {
|
||||
this.hass
|
||||
.callApi(
|
||||
"POST",
|
||||
`hassio/${
|
||||
atLeastVersion(this.hass.config.version, 2021, 9)
|
||||
? "backups"
|
||||
: "snapshots"
|
||||
}/${this._backup!.slug}/restore/full`,
|
||||
backupDetails
|
||||
)
|
||||
.then(
|
||||
() => {
|
||||
this.closeDialog();
|
||||
},
|
||||
(error) => {
|
||||
this._error = error.body.message;
|
||||
}
|
||||
);
|
||||
} else {
|
||||
fireEvent(this, "restoring");
|
||||
fetch(`/api/hassio/backups/${this._backup!.slug}/restore/full`, {
|
||||
method: "POST",
|
||||
body: JSON.stringify(backupDetails),
|
||||
});
|
||||
this.closeDialog();
|
||||
}
|
||||
}
|
||||
|
||||
private async _deleteClicked() {
|
||||
if (
|
||||
!(await showConfirmationDialog(this, {
|
||||
title: "Are you sure you want to delete this backup?",
|
||||
confirmText: "delete",
|
||||
dismissText: "cancel",
|
||||
}))
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.hass
|
||||
|
||||
.callApi(
|
||||
atLeastVersion(this.hass.config.version, 2021, 9) ? "DELETE" : "POST",
|
||||
`hassio/${
|
||||
atLeastVersion(this.hass.config.version, 2021, 9)
|
||||
? `backups/${this._backup!.slug}`
|
||||
: `snapshots/${this._backup!.slug}/remove`
|
||||
}`
|
||||
)
|
||||
.then(
|
||||
() => {
|
||||
if (this._dialogParams!.onDelete) {
|
||||
this._dialogParams!.onDelete();
|
||||
}
|
||||
this.closeDialog();
|
||||
},
|
||||
(error) => {
|
||||
this._error = error.body.message;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
private async _downloadClicked() {
|
||||
let signedPath: { path: string };
|
||||
try {
|
||||
signedPath = await getSignedPath(
|
||||
this.hass,
|
||||
`/api/hassio/${
|
||||
atLeastVersion(this.hass.config.version, 2021, 9)
|
||||
? "backups"
|
||||
: "snapshots"
|
||||
}/${this._backup!.slug}/download`
|
||||
);
|
||||
} catch (err: any) {
|
||||
await showAlertDialog(this, {
|
||||
text: extractApiErrorMessage(err),
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (window.location.href.includes("ui.nabu.casa")) {
|
||||
const confirm = await showConfirmationDialog(this, {
|
||||
title: "Potential slow download",
|
||||
text: "Downloading backups over the Nabu Casa URL will take some time, it is recomended to use your local URL instead, do you want to continue?",
|
||||
confirmText: "continue",
|
||||
dismissText: "cancel",
|
||||
});
|
||||
if (!confirm) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
fileDownload(
|
||||
this,
|
||||
signedPath.path,
|
||||
`home_assistant_backup_${slugify(this._computeName)}.tar`
|
||||
);
|
||||
}
|
||||
|
||||
private get _computeName() {
|
||||
return this._backup
|
||||
? this._backup.name || this._backup.slug
|
||||
: "Unnamed backup";
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"dialog-hassio-backup": HassioBackupDialog;
|
||||
}
|
||||
}
|
@@ -1,19 +0,0 @@
|
||||
import { fireEvent } from "../../../../src/common/dom/fire_event";
|
||||
import "./dialog-hassio-backup-upload";
|
||||
|
||||
export interface HassioBackupUploadDialogParams {
|
||||
showBackup: (slug: string) => void;
|
||||
reloadBackup?: () => Promise<void>;
|
||||
onboarding?: boolean;
|
||||
}
|
||||
|
||||
export const showBackupUploadDialog = (
|
||||
element: HTMLElement,
|
||||
dialogParams: HassioBackupUploadDialogParams
|
||||
): void => {
|
||||
fireEvent(element, "show-dialog", {
|
||||
dialogTag: "dialog-hassio-backup-upload",
|
||||
dialogImport: () => import("./dialog-hassio-backup-upload"),
|
||||
dialogParams,
|
||||
});
|
||||
};
|
@@ -1,193 +0,0 @@
|
||||
import "@polymer/paper-dropdown-menu/paper-dropdown-menu";
|
||||
import "@polymer/paper-item/paper-item";
|
||||
import "@polymer/paper-listbox/paper-listbox";
|
||||
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import memoizeOne from "memoize-one";
|
||||
import { fireEvent } from "../../../../src/common/dom/fire_event";
|
||||
import "../../../../src/components/ha-circular-progress";
|
||||
import "../../../../src/components/ha-markdown";
|
||||
import {
|
||||
extractApiErrorMessage,
|
||||
ignoreSupervisorError,
|
||||
} from "../../../../src/data/hassio/common";
|
||||
import {
|
||||
DatadiskList,
|
||||
listDatadisks,
|
||||
moveDatadisk,
|
||||
} from "../../../../src/data/hassio/host";
|
||||
import { Supervisor } from "../../../../src/data/supervisor/supervisor";
|
||||
import { showAlertDialog } from "../../../../src/dialogs/generic/show-dialog-box";
|
||||
import { haStyle, haStyleDialog } from "../../../../src/resources/styles";
|
||||
import { HomeAssistant } from "../../../../src/types";
|
||||
import { HassioDatatiskDialogParams } from "./show-dialog-hassio-datadisk";
|
||||
|
||||
const calculateMoveTime = memoizeOne((supervisor: Supervisor): number => {
|
||||
const speed = supervisor.host.disk_life_time !== "" ? 30 : 10;
|
||||
const moveTime = (supervisor.host.disk_used * 1000) / 60 / speed;
|
||||
const rebootTime = (supervisor.host.startup_time * 4) / 60;
|
||||
return Math.ceil((moveTime + rebootTime) / 10) * 10;
|
||||
});
|
||||
|
||||
@customElement("dialog-hassio-datadisk")
|
||||
class HassioDatadiskDialog extends LitElement {
|
||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||
|
||||
@state() private dialogParams?: HassioDatatiskDialogParams;
|
||||
|
||||
@state() private selectedDevice?: string;
|
||||
|
||||
@state() private devices?: DatadiskList["devices"];
|
||||
|
||||
@state() private moving = false;
|
||||
|
||||
public showDialog(params: HassioDatatiskDialogParams) {
|
||||
this.dialogParams = params;
|
||||
listDatadisks(this.hass).then((data) => {
|
||||
this.devices = data.devices;
|
||||
});
|
||||
}
|
||||
|
||||
public closeDialog(): void {
|
||||
this.dialogParams = undefined;
|
||||
this.selectedDevice = undefined;
|
||||
this.devices = undefined;
|
||||
this.moving = false;
|
||||
fireEvent(this, "dialog-closed", { dialog: this.localName });
|
||||
}
|
||||
|
||||
protected render(): TemplateResult {
|
||||
if (!this.dialogParams) {
|
||||
return html``;
|
||||
}
|
||||
let heading: string;
|
||||
let content: TemplateResult;
|
||||
|
||||
if (this.moving) {
|
||||
heading = this.dialogParams.supervisor.localize(
|
||||
"dialog.datadisk_move.moving"
|
||||
);
|
||||
content = html`
|
||||
<ha-circular-progress alt="Moving" size="large" active>
|
||||
</ha-circular-progress>
|
||||
<p class="progress-text">
|
||||
${this.dialogParams.supervisor.localize(
|
||||
"dialog.datadisk_move.moving_desc"
|
||||
)}
|
||||
</p>
|
||||
`;
|
||||
} else {
|
||||
heading = this.dialogParams.supervisor.localize(
|
||||
"dialog.datadisk_move.title"
|
||||
);
|
||||
content = html`
|
||||
${this.devices?.length
|
||||
? html`
|
||||
${this.dialogParams.supervisor.localize(
|
||||
"dialog.datadisk_move.description",
|
||||
{
|
||||
current_path: this.dialogParams.supervisor.os.data_disk,
|
||||
time: calculateMoveTime(this.dialogParams.supervisor),
|
||||
}
|
||||
)}
|
||||
<br /><br />
|
||||
|
||||
<paper-dropdown-menu
|
||||
.label=${this.dialogParams.supervisor.localize(
|
||||
"dialog.datadisk_move.select_device"
|
||||
)}
|
||||
@value-changed=${this._select_device}
|
||||
>
|
||||
<paper-listbox slot="dropdown-content">
|
||||
${this.devices.map(
|
||||
(device) => html`<paper-item>${device}</paper-item>`
|
||||
)}
|
||||
</paper-listbox>
|
||||
</paper-dropdown-menu>
|
||||
`
|
||||
: this.devices === undefined
|
||||
? this.dialogParams.supervisor.localize(
|
||||
"dialog.datadisk_move.loading_devices"
|
||||
)
|
||||
: this.dialogParams.supervisor.localize(
|
||||
"dialog.datadisk_move.no_devices"
|
||||
)}
|
||||
|
||||
<mwc-button slot="secondaryAction" @click=${this.closeDialog}>
|
||||
${this.dialogParams.supervisor.localize(
|
||||
"dialog.datadisk_move.cancel"
|
||||
)}
|
||||
</mwc-button>
|
||||
|
||||
<mwc-button
|
||||
.disabled=${!this.selectedDevice}
|
||||
slot="primaryAction"
|
||||
@click=${this._moveDatadisk}
|
||||
>
|
||||
${this.dialogParams.supervisor.localize("dialog.datadisk_move.move")}
|
||||
</mwc-button>
|
||||
`;
|
||||
}
|
||||
|
||||
return html`
|
||||
<ha-dialog
|
||||
open
|
||||
scrimClickAction
|
||||
escapeKeyAction
|
||||
@closed=${this.closeDialog}
|
||||
?hideActions=${this.moving}
|
||||
.heading=${heading}
|
||||
>
|
||||
${content}
|
||||
</ha-dialog>
|
||||
`;
|
||||
}
|
||||
|
||||
private _select_device(event) {
|
||||
this.selectedDevice = event.detail.value;
|
||||
}
|
||||
|
||||
private async _moveDatadisk() {
|
||||
this.moving = true;
|
||||
try {
|
||||
await moveDatadisk(this.hass, this.selectedDevice!);
|
||||
} catch (err: any) {
|
||||
if (this.hass.connection.connected && !ignoreSupervisorError(err)) {
|
||||
showAlertDialog(this, {
|
||||
title: this.dialogParams!.supervisor.localize(
|
||||
"system.host.failed_to_move"
|
||||
),
|
||||
text: extractApiErrorMessage(err),
|
||||
});
|
||||
this.closeDialog();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static get styles(): CSSResultGroup {
|
||||
return [
|
||||
haStyle,
|
||||
haStyleDialog,
|
||||
css`
|
||||
paper-dropdown-menu {
|
||||
width: 100%;
|
||||
}
|
||||
ha-circular-progress {
|
||||
display: block;
|
||||
margin: 32px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.progress-text {
|
||||
text-align: center;
|
||||
}
|
||||
`,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"dialog-hassio-datadisk": HassioDatadiskDialog;
|
||||
}
|
||||
}
|
@@ -1,17 +0,0 @@
|
||||
import { fireEvent } from "../../../../src/common/dom/fire_event";
|
||||
import { Supervisor } from "../../../../src/data/supervisor/supervisor";
|
||||
|
||||
export interface HassioDatatiskDialogParams {
|
||||
supervisor: Supervisor;
|
||||
}
|
||||
|
||||
export const showHassioDatadiskDialog = (
|
||||
element: HTMLElement,
|
||||
dialogParams: HassioDatatiskDialogParams
|
||||
): void => {
|
||||
fireEvent(element, "show-dialog", {
|
||||
dialogTag: "dialog-hassio-datadisk",
|
||||
dialogImport: () => import("./dialog-hassio-datadisk"),
|
||||
dialogParams,
|
||||
});
|
||||
};
|
@@ -1,194 +0,0 @@
|
||||
import { mdiClose } from "@mdi/js";
|
||||
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import memoizeOne from "memoize-one";
|
||||
import { fireEvent } from "../../../../src/common/dom/fire_event";
|
||||
import "../../../../src/common/search/search-input";
|
||||
import { stringCompare } from "../../../../src/common/string/compare";
|
||||
import "../../../../src/components/ha-dialog";
|
||||
import "../../../../src/components/ha-expansion-panel";
|
||||
import { HassioHardwareInfo } from "../../../../src/data/hassio/hardware";
|
||||
import { dump } from "../../../../src/resources/js-yaml-dump";
|
||||
import { haStyle, haStyleDialog } from "../../../../src/resources/styles";
|
||||
import { HomeAssistant } from "../../../../src/types";
|
||||
import { HassioHardwareDialogParams } from "./show-dialog-hassio-hardware";
|
||||
|
||||
const _filterDevices = memoizeOne(
|
||||
(showAdvanced: boolean, hardware: HassioHardwareInfo, filter: string) =>
|
||||
hardware.devices
|
||||
.filter(
|
||||
(device) =>
|
||||
(showAdvanced ||
|
||||
["tty", "gpio", "input"].includes(device.subsystem)) &&
|
||||
(device.by_id?.toLowerCase().includes(filter) ||
|
||||
device.name.toLowerCase().includes(filter) ||
|
||||
device.dev_path.toLocaleLowerCase().includes(filter) ||
|
||||
JSON.stringify(device.attributes)
|
||||
.toLocaleLowerCase()
|
||||
.includes(filter))
|
||||
)
|
||||
.sort((a, b) => stringCompare(a.name, b.name))
|
||||
);
|
||||
|
||||
@customElement("dialog-hassio-hardware")
|
||||
class HassioHardwareDialog extends LitElement {
|
||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||
|
||||
@state() private _dialogParams?: HassioHardwareDialogParams;
|
||||
|
||||
@state() private _filter?: string;
|
||||
|
||||
public showDialog(params: HassioHardwareDialogParams) {
|
||||
this._dialogParams = params;
|
||||
}
|
||||
|
||||
public closeDialog() {
|
||||
this._dialogParams = undefined;
|
||||
fireEvent(this, "dialog-closed", { dialog: this.localName });
|
||||
}
|
||||
|
||||
protected render(): TemplateResult {
|
||||
if (!this._dialogParams) {
|
||||
return html``;
|
||||
}
|
||||
|
||||
const devices = _filterDevices(
|
||||
this.hass.userData?.showAdvanced || false,
|
||||
this._dialogParams.hardware,
|
||||
(this._filter || "").toLowerCase()
|
||||
);
|
||||
|
||||
return html`
|
||||
<ha-dialog
|
||||
open
|
||||
scrimClickAction
|
||||
hideActions
|
||||
@closed=${this.closeDialog}
|
||||
.heading=${true}
|
||||
>
|
||||
<div class="header" slot="heading">
|
||||
<h2>
|
||||
${this._dialogParams.supervisor.localize("dialog.hardware.title")}
|
||||
</h2>
|
||||
<mwc-icon-button dialogAction="close">
|
||||
<ha-svg-icon .path=${mdiClose}></ha-svg-icon>
|
||||
</mwc-icon-button>
|
||||
<search-input
|
||||
autofocus
|
||||
no-label-float
|
||||
.filter=${this._filter}
|
||||
@value-changed=${this._handleSearchChange}
|
||||
.label=${this._dialogParams.supervisor.localize(
|
||||
"dialog.hardware.search"
|
||||
)}
|
||||
>
|
||||
</search-input>
|
||||
</div>
|
||||
|
||||
${devices.map(
|
||||
(device) =>
|
||||
html`<ha-expansion-panel
|
||||
.header=${device.name}
|
||||
.secondary=${device.by_id || undefined}
|
||||
outlined
|
||||
>
|
||||
<div class="device-property">
|
||||
<span>
|
||||
${this._dialogParams!.supervisor.localize(
|
||||
"dialog.hardware.subsystem"
|
||||
)}:
|
||||
</span>
|
||||
<span>${device.subsystem}</span>
|
||||
</div>
|
||||
<div class="device-property">
|
||||
<span>
|
||||
${this._dialogParams!.supervisor.localize(
|
||||
"dialog.hardware.device_path"
|
||||
)}:
|
||||
</span>
|
||||
<code>${device.dev_path}</code>
|
||||
</div>
|
||||
${device.by_id
|
||||
? html` <div class="device-property">
|
||||
<span>
|
||||
${this._dialogParams!.supervisor.localize(
|
||||
"dialog.hardware.id"
|
||||
)}:
|
||||
</span>
|
||||
<code>${device.by_id}</code>
|
||||
</div>`
|
||||
: ""}
|
||||
<div class="attributes">
|
||||
<span>
|
||||
${this._dialogParams!.supervisor.localize(
|
||||
"dialog.hardware.attributes"
|
||||
)}:
|
||||
</span>
|
||||
<pre>${dump(device.attributes, { indent: 2 })}</pre>
|
||||
</div>
|
||||
</ha-expansion-panel>`
|
||||
)}
|
||||
</ha-dialog>
|
||||
`;
|
||||
}
|
||||
|
||||
private _handleSearchChange(ev: CustomEvent) {
|
||||
this._filter = ev.detail.value;
|
||||
}
|
||||
|
||||
static get styles(): CSSResultGroup {
|
||||
return [
|
||||
haStyle,
|
||||
haStyleDialog,
|
||||
css`
|
||||
mwc-icon-button {
|
||||
position: absolute;
|
||||
right: 16px;
|
||||
top: 10px;
|
||||
text-decoration: none;
|
||||
color: var(--primary-text-color);
|
||||
}
|
||||
h2 {
|
||||
margin: 18px 42px 0 18px;
|
||||
color: var(--primary-text-color);
|
||||
}
|
||||
|
||||
ha-expansion-panel {
|
||||
margin: 4px 0;
|
||||
}
|
||||
pre,
|
||||
code {
|
||||
background-color: var(--markdown-code-background-color, none);
|
||||
border-radius: 3px;
|
||||
}
|
||||
pre {
|
||||
padding: 16px;
|
||||
overflow: auto;
|
||||
line-height: 1.45;
|
||||
font-family: var(--code-font-family, monospace);
|
||||
}
|
||||
code {
|
||||
font-size: 85%;
|
||||
padding: 0.2em 0.4em;
|
||||
}
|
||||
search-input {
|
||||
margin: 0 16px;
|
||||
display: block;
|
||||
}
|
||||
.device-property {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
.attributes {
|
||||
margin-top: 12px;
|
||||
}
|
||||
`,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"dialog-hassio-hardware": HassioHardwareDialog;
|
||||
}
|
||||
}
|
@@ -1,19 +0,0 @@
|
||||
import { fireEvent } from "../../../../src/common/dom/fire_event";
|
||||
import { HassioHardwareInfo } from "../../../../src/data/hassio/hardware";
|
||||
import { Supervisor } from "../../../../src/data/supervisor/supervisor";
|
||||
|
||||
export interface HassioHardwareDialogParams {
|
||||
supervisor: Supervisor;
|
||||
hardware: HassioHardwareInfo;
|
||||
}
|
||||
|
||||
export const showHassioHardwareDialog = (
|
||||
element: HTMLElement,
|
||||
dialogParams: HassioHardwareDialogParams
|
||||
): void => {
|
||||
fireEvent(element, "show-dialog", {
|
||||
dialogTag: "dialog-hassio-hardware",
|
||||
dialogImport: () => import("./dialog-hassio-hardware"),
|
||||
dialogParams,
|
||||
});
|
||||
};
|
@@ -61,6 +61,10 @@ class HassioMarkdownDialog extends LitElement {
|
||||
app-toolbar [main-title] {
|
||||
margin-left: 16px;
|
||||
}
|
||||
paper-checkbox {
|
||||
display: block;
|
||||
margin: 4px;
|
||||
}
|
||||
@media all and (max-width: 450px), all and (max-height: 500px) {
|
||||
ha-paper-dialog {
|
||||
max-height: 100%;
|
||||
|
@@ -10,7 +10,6 @@ import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { cache } from "lit/directives/cache";
|
||||
import { fireEvent } from "../../../../src/common/dom/fire_event";
|
||||
import "../../../../src/components/ha-alert";
|
||||
import "../../../../src/components/ha-circular-progress";
|
||||
import "../../../../src/components/ha-dialog";
|
||||
import "../../../../src/components/ha-expansion-panel";
|
||||
@@ -42,8 +41,7 @@ const IP_VERSIONS = ["ipv4", "ipv6"];
|
||||
@customElement("dialog-hassio-network")
|
||||
export class DialogHassioNetwork
|
||||
extends LitElement
|
||||
implements HassDialog<HassioNetworkDialogParams>
|
||||
{
|
||||
implements HassDialog<HassioNetworkDialogParams> {
|
||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||
|
||||
@property({ attribute: false }) public supervisor!: Supervisor;
|
||||
@@ -109,7 +107,7 @@ export class DialogHassioNetwork
|
||||
</mwc-icon-button>
|
||||
</ha-header-bar>
|
||||
${this._interfaces.length > 1
|
||||
? html`<mwc-tab-bar
|
||||
? html` <mwc-tab-bar
|
||||
.activeIndex=${this._curTabIndex}
|
||||
@MDCTabBar:activated=${this._handleTabActivated}
|
||||
>${this._interfaces.map(
|
||||
@@ -252,9 +250,9 @@ export class DialogHassioNetwork
|
||||
`
|
||||
: ""}
|
||||
${this._dirty
|
||||
? html`<ha-alert alert-type="warning">
|
||||
? html`<div class="warning">
|
||||
${this.supervisor.localize("dialog.network.warning")}
|
||||
</ha-alert>`
|
||||
</div>`
|
||||
: ""}
|
||||
</div>
|
||||
<div class="buttons">
|
||||
@@ -287,7 +285,7 @@ export class DialogHassioNetwork
|
||||
this.hass,
|
||||
this._interface.interface
|
||||
);
|
||||
} catch (err: any) {
|
||||
} catch (err) {
|
||||
showAlertDialog(this, {
|
||||
title: "Failed to scan for accesspoints",
|
||||
text: extractApiErrorMessage(err),
|
||||
@@ -448,7 +446,7 @@ export class DialogHassioNetwork
|
||||
this._interface!.interface,
|
||||
interfaceOptions
|
||||
);
|
||||
} catch (err: any) {
|
||||
} catch (err) {
|
||||
showAlertDialog(this, {
|
||||
title: this.supervisor.localize("dialog.network.failed_to_change"),
|
||||
text: extractApiErrorMessage(err),
|
||||
@@ -494,7 +492,7 @@ export class DialogHassioNetwork
|
||||
}
|
||||
|
||||
private _handleRadioValueChangedAp(ev: CustomEvent): void {
|
||||
const value = (ev.target as any).value as string as
|
||||
const value = ((ev.target as any).value as string) as
|
||||
| "open"
|
||||
| "wep"
|
||||
| "wpa-psk";
|
||||
|
@@ -161,9 +161,9 @@ class HassioRegistriesDialog extends LitElement {
|
||||
|
||||
public focus(): void {
|
||||
this.updateComplete.then(() =>
|
||||
(
|
||||
this.shadowRoot?.querySelector("[dialogInitialFocus]") as HTMLElement
|
||||
)?.focus()
|
||||
(this.shadowRoot?.querySelector(
|
||||
"[dialogInitialFocus]"
|
||||
) as HTMLElement)?.focus()
|
||||
);
|
||||
}
|
||||
|
||||
@@ -190,7 +190,7 @@ class HassioRegistriesDialog extends LitElement {
|
||||
await addHassioDockerRegistry(this.hass, data);
|
||||
await this._loadRegistries();
|
||||
this._addingRegistry = false;
|
||||
} catch (err: any) {
|
||||
} catch (err) {
|
||||
showAlertDialog(this, {
|
||||
title: this.supervisor.localize("dialog.registries.failed_to_add"),
|
||||
text: extractApiErrorMessage(err),
|
||||
@@ -204,7 +204,7 @@ class HassioRegistriesDialog extends LitElement {
|
||||
try {
|
||||
await removeHassioDockerRegistry(this.hass, entry.registry);
|
||||
await this._loadRegistries();
|
||||
} catch (err: any) {
|
||||
} catch (err) {
|
||||
showAlertDialog(this, {
|
||||
title: this.supervisor.localize("dialog.registries.failed_to_remove"),
|
||||
text: extractApiErrorMessage(err),
|
||||
|
@@ -9,7 +9,6 @@ import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
||||
import { customElement, property, query, state } from "lit/decorators";
|
||||
import memoizeOne from "memoize-one";
|
||||
import { fireEvent } from "../../../../src/common/dom/fire_event";
|
||||
import "../../../../src/components/ha-alert";
|
||||
import "../../../../src/components/ha-circular-progress";
|
||||
import { createCloseHeading } from "../../../../src/components/ha-dialog";
|
||||
import "../../../../src/components/ha-svg-icon";
|
||||
@@ -76,9 +75,7 @@ class HassioRepositoriesDialog extends LitElement {
|
||||
this._dialogParams!.supervisor.localize("dialog.repositories.title")
|
||||
)}
|
||||
>
|
||||
${this._error
|
||||
? html`<ha-alert alert-type="error">${this._error}</ha-alert>`
|
||||
: ""}
|
||||
${this._error ? html`<div class="error">${this._error}</div>` : ""}
|
||||
<div class="form">
|
||||
${repositories.length
|
||||
? repositories.map(
|
||||
@@ -164,9 +161,9 @@ class HassioRepositoriesDialog extends LitElement {
|
||||
|
||||
public focus() {
|
||||
this.updateComplete.then(() =>
|
||||
(
|
||||
this.shadowRoot?.querySelector("[dialogInitialFocus]") as HTMLElement
|
||||
)?.focus()
|
||||
(this.shadowRoot?.querySelector(
|
||||
"[dialogInitialFocus]"
|
||||
) as HTMLElement)?.focus()
|
||||
);
|
||||
}
|
||||
|
||||
@@ -185,7 +182,7 @@ class HassioRepositoriesDialog extends LitElement {
|
||||
this._repositories = addonsinfo.repositories;
|
||||
|
||||
fireEvent(this, "supervisor-collection-refresh", { collection: "addon" });
|
||||
} catch (err: any) {
|
||||
} catch (err) {
|
||||
this._error = extractApiErrorMessage(err);
|
||||
}
|
||||
}
|
||||
@@ -207,7 +204,7 @@ class HassioRepositoriesDialog extends LitElement {
|
||||
await this._loadData();
|
||||
|
||||
input.value = "";
|
||||
} catch (err: any) {
|
||||
} catch (err) {
|
||||
this._error = extractApiErrorMessage(err);
|
||||
}
|
||||
this._processing = false;
|
||||
@@ -229,7 +226,7 @@ class HassioRepositoriesDialog extends LitElement {
|
||||
addons_repositories: newRepositories,
|
||||
});
|
||||
await this._loadData();
|
||||
} catch (err: any) {
|
||||
} catch (err) {
|
||||
this._error = extractApiErrorMessage(err);
|
||||
}
|
||||
}
|
||||
|
88
hassio/src/dialogs/backup/dialog-hassio-create-backup.ts → hassio/src/dialogs/snapshot/dialog-hassio-create-snapshot.ts
Normal file → Executable file
88
hassio/src/dialogs/backup/dialog-hassio-create-backup.ts → hassio/src/dialogs/snapshot/dialog-hassio-create-snapshot.ts
Normal file → Executable file
@@ -2,42 +2,41 @@ import "@material/mwc-button";
|
||||
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
||||
import { customElement, property, query, state } from "lit/decorators";
|
||||
import { fireEvent } from "../../../../src/common/dom/fire_event";
|
||||
import "../../../../src/components/ha-alert";
|
||||
import "../../../../src/components/buttons/ha-progress-button";
|
||||
import { createCloseHeading } from "../../../../src/components/ha-dialog";
|
||||
import { extractApiErrorMessage } from "../../../../src/data/hassio/common";
|
||||
import {
|
||||
createHassioFullBackup,
|
||||
createHassioPartialBackup,
|
||||
} from "../../../../src/data/hassio/backup";
|
||||
createHassioFullSnapshot,
|
||||
createHassioPartialSnapshot,
|
||||
} from "../../../../src/data/hassio/snapshot";
|
||||
import { showAlertDialog } from "../../../../src/dialogs/generic/show-dialog-box";
|
||||
import { haStyle, haStyleDialog } from "../../../../src/resources/styles";
|
||||
import { HomeAssistant } from "../../../../src/types";
|
||||
import "../../components/supervisor-backup-content";
|
||||
import type { SupervisorBackupContent } from "../../components/supervisor-backup-content";
|
||||
import { HassioCreateBackupDialogParams } from "./show-dialog-hassio-create-backup";
|
||||
import "../../components/supervisor-snapshot-content";
|
||||
import type { SupervisorSnapshotContent } from "../../components/supervisor-snapshot-content";
|
||||
import { HassioCreateSnapshotDialogParams } from "./show-dialog-hassio-create-snapshot";
|
||||
|
||||
@customElement("dialog-hassio-create-backup")
|
||||
class HassioCreateBackupDialog extends LitElement {
|
||||
@customElement("dialog-hassio-create-snapshot")
|
||||
class HassioCreateSnapshotDialog extends LitElement {
|
||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||
|
||||
@state() private _dialogParams?: HassioCreateBackupDialogParams;
|
||||
@state() private _dialogParams?: HassioCreateSnapshotDialogParams;
|
||||
|
||||
@state() private _error?: string;
|
||||
|
||||
@state() private _creatingBackup = false;
|
||||
@state() private _creatingSnapshot = false;
|
||||
|
||||
@query("supervisor-backup-content")
|
||||
private _backupContent!: SupervisorBackupContent;
|
||||
@query("supervisor-snapshot-content")
|
||||
private _snapshotContent!: SupervisorSnapshotContent;
|
||||
|
||||
public showDialog(params: HassioCreateBackupDialogParams) {
|
||||
public showDialog(params: HassioCreateSnapshotDialogParams) {
|
||||
this._dialogParams = params;
|
||||
this._creatingBackup = false;
|
||||
this._creatingSnapshot = false;
|
||||
}
|
||||
|
||||
public closeDialog() {
|
||||
this._dialogParams = undefined;
|
||||
this._creatingBackup = false;
|
||||
this._creatingSnapshot = false;
|
||||
this._error = undefined;
|
||||
fireEvent(this, "dialog-closed", { dialog: this.localName });
|
||||
}
|
||||
@@ -53,84 +52,73 @@ class HassioCreateBackupDialog extends LitElement {
|
||||
@closed=${this.closeDialog}
|
||||
.heading=${createCloseHeading(
|
||||
this.hass,
|
||||
this._dialogParams.supervisor.localize("backup.create_backup")
|
||||
this._dialogParams.supervisor.localize("snapshot.create_snapshot")
|
||||
)}
|
||||
>
|
||||
${this._creatingBackup
|
||||
${this._creatingSnapshot
|
||||
? html` <ha-circular-progress active></ha-circular-progress>`
|
||||
: html`<supervisor-backup-content
|
||||
: html`<supervisor-snapshot-content
|
||||
.hass=${this.hass}
|
||||
.supervisor=${this._dialogParams.supervisor}
|
||||
>
|
||||
</supervisor-backup-content>`}
|
||||
${this._error
|
||||
? html`<ha-alert alert-type="error">${this._error}</ha-alert>`
|
||||
: ""}
|
||||
</supervisor-snapshot-content>`}
|
||||
${this._error ? html`<p class="error">Error: ${this._error}</p>` : ""}
|
||||
<mwc-button slot="secondaryAction" @click=${this.closeDialog}>
|
||||
${this._dialogParams.supervisor.localize("common.close")}
|
||||
</mwc-button>
|
||||
<mwc-button
|
||||
.disabled=${this._creatingBackup}
|
||||
.disabled=${this._creatingSnapshot}
|
||||
slot="primaryAction"
|
||||
@click=${this._createBackup}
|
||||
@click=${this._createSnapshot}
|
||||
>
|
||||
${this._dialogParams.supervisor.localize("backup.create")}
|
||||
${this._dialogParams.supervisor.localize("snapshot.create")}
|
||||
</mwc-button>
|
||||
</ha-dialog>
|
||||
`;
|
||||
}
|
||||
|
||||
private async _createBackup(): Promise<void> {
|
||||
private async _createSnapshot(): Promise<void> {
|
||||
if (this._dialogParams!.supervisor.info.state !== "running") {
|
||||
showAlertDialog(this, {
|
||||
title: this._dialogParams!.supervisor.localize(
|
||||
"backup.could_not_create"
|
||||
"snapshot.could_not_create"
|
||||
),
|
||||
text: this._dialogParams!.supervisor.localize(
|
||||
"backup.create_blocked_not_running",
|
||||
"snapshot.create_blocked_not_running",
|
||||
"state",
|
||||
this._dialogParams!.supervisor.info.state
|
||||
),
|
||||
});
|
||||
return;
|
||||
}
|
||||
const backupDetails = this._backupContent.backupDetails();
|
||||
this._creatingBackup = true;
|
||||
const snapshotDetails = this._snapshotContent.snapshotDetails();
|
||||
this._creatingSnapshot = true;
|
||||
|
||||
this._error = "";
|
||||
if (backupDetails.password && !backupDetails.password.length) {
|
||||
this._error = this._dialogParams!.supervisor.localize(
|
||||
"backup.enter_password"
|
||||
);
|
||||
this._creatingBackup = false;
|
||||
return;
|
||||
}
|
||||
if (
|
||||
backupDetails.password &&
|
||||
backupDetails.password !== backupDetails.confirm_password
|
||||
this._snapshotContent.snapshotHasPassword &&
|
||||
!this._snapshotContent.snapshotPassword.length
|
||||
) {
|
||||
this._error = this._dialogParams!.supervisor.localize(
|
||||
"backup.passwords_not_matching"
|
||||
"snapshot.enter_password"
|
||||
);
|
||||
this._creatingBackup = false;
|
||||
this._creatingSnapshot = false;
|
||||
return;
|
||||
}
|
||||
|
||||
delete backupDetails.confirm_password;
|
||||
|
||||
try {
|
||||
if (this._backupContent.backupType === "full") {
|
||||
await createHassioFullBackup(this.hass, backupDetails);
|
||||
if (this._snapshotContent.snapshotType === "full") {
|
||||
await createHassioFullSnapshot(this.hass, snapshotDetails);
|
||||
} else {
|
||||
await createHassioPartialBackup(this.hass, backupDetails);
|
||||
await createHassioPartialSnapshot(this.hass, snapshotDetails);
|
||||
}
|
||||
|
||||
this._dialogParams!.onCreate();
|
||||
this.closeDialog();
|
||||
} catch (err: any) {
|
||||
} catch (err) {
|
||||
this._error = extractApiErrorMessage(err);
|
||||
}
|
||||
this._creatingBackup = false;
|
||||
this._creatingSnapshot = false;
|
||||
}
|
||||
|
||||
static get styles(): CSSResultGroup {
|
||||
@@ -149,6 +137,6 @@ class HassioCreateBackupDialog extends LitElement {
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"dialog-hassio-create-backup": HassioCreateBackupDialog;
|
||||
"dialog-hassio-create-snapshot": HassioCreateSnapshotDialog;
|
||||
}
|
||||
}
|
@@ -6,20 +6,19 @@ import "../../../../src/components/ha-header-bar";
|
||||
import { HassDialog } from "../../../../src/dialogs/make-dialog-manager";
|
||||
import { haStyleDialog } from "../../../../src/resources/styles";
|
||||
import type { HomeAssistant } from "../../../../src/types";
|
||||
import "../../components/hassio-upload-backup";
|
||||
import { HassioBackupUploadDialogParams } from "./show-dialog-backup-upload";
|
||||
import "../../components/hassio-upload-snapshot";
|
||||
import { HassioSnapshotUploadDialogParams } from "./show-dialog-snapshot-upload";
|
||||
|
||||
@customElement("dialog-hassio-backup-upload")
|
||||
export class DialogHassioBackupUpload
|
||||
@customElement("dialog-hassio-snapshot-upload")
|
||||
export class DialogHassioSnapshotUpload
|
||||
extends LitElement
|
||||
implements HassDialog<HassioBackupUploadDialogParams>
|
||||
{
|
||||
implements HassDialog<HassioSnapshotUploadDialogParams> {
|
||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||
|
||||
@state() private _params?: HassioBackupUploadDialogParams;
|
||||
@state() private _params?: HassioSnapshotUploadDialogParams;
|
||||
|
||||
public async showDialog(
|
||||
params: HassioBackupUploadDialogParams
|
||||
params: HassioSnapshotUploadDialogParams
|
||||
): Promise<void> {
|
||||
this._params = params;
|
||||
await this.updateComplete;
|
||||
@@ -27,8 +26,8 @@ export class DialogHassioBackupUpload
|
||||
|
||||
public closeDialog(): void {
|
||||
if (this._params && !this._params.onboarding) {
|
||||
if (this._params.reloadBackup) {
|
||||
this._params.reloadBackup();
|
||||
if (this._params.reloadSnapshot) {
|
||||
this._params.reloadSnapshot();
|
||||
}
|
||||
}
|
||||
this._params = undefined;
|
||||
@@ -51,23 +50,23 @@ export class DialogHassioBackupUpload
|
||||
>
|
||||
<div slot="heading">
|
||||
<ha-header-bar>
|
||||
<span slot="title"> Upload backup </span>
|
||||
<span slot="title"> Upload snapshot </span>
|
||||
<mwc-icon-button slot="actionItems" dialogAction="cancel">
|
||||
<ha-svg-icon .path=${mdiClose}></ha-svg-icon>
|
||||
</mwc-icon-button>
|
||||
</ha-header-bar>
|
||||
</div>
|
||||
<hassio-upload-backup
|
||||
@backup-uploaded=${this._backupUploaded}
|
||||
<hassio-upload-snapshot
|
||||
@snapshot-uploaded=${this._snapshotUploaded}
|
||||
.hass=${this.hass}
|
||||
></hassio-upload-backup>
|
||||
></hassio-upload-snapshot>
|
||||
</ha-dialog>
|
||||
`;
|
||||
}
|
||||
|
||||
private _backupUploaded(ev) {
|
||||
const backup = ev.detail.backup;
|
||||
this._params?.showBackup(backup.slug);
|
||||
private _snapshotUploaded(ev) {
|
||||
const snapshot = ev.detail.snapshot;
|
||||
this._params?.showSnapshot(snapshot.slug);
|
||||
this.closeDialog();
|
||||
}
|
||||
|
||||
@@ -94,6 +93,6 @@ export class DialogHassioBackupUpload
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"dialog-hassio-backup-upload": DialogHassioBackupUpload;
|
||||
"dialog-hassio-snapshot-upload": DialogHassioSnapshotUpload;
|
||||
}
|
||||
}
|
310
hassio/src/dialogs/snapshot/dialog-hassio-snapshot.ts
Executable file
310
hassio/src/dialogs/snapshot/dialog-hassio-snapshot.ts
Executable file
@@ -0,0 +1,310 @@
|
||||
import { ActionDetail } from "@material/mwc-list";
|
||||
import "@material/mwc-list/mwc-list-item";
|
||||
import { mdiDotsVertical } from "@mdi/js";
|
||||
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
||||
import { customElement, property, query, state } from "lit/decorators";
|
||||
import { fireEvent } from "../../../../src/common/dom/fire_event";
|
||||
import { slugify } from "../../../../src/common/string/slugify";
|
||||
import "../../../../src/components/buttons/ha-progress-button";
|
||||
import "../../../../src/components/ha-button-menu";
|
||||
import { createCloseHeading } from "../../../../src/components/ha-dialog";
|
||||
import "../../../../src/components/ha-svg-icon";
|
||||
import { getSignedPath } from "../../../../src/data/auth";
|
||||
import { extractApiErrorMessage } from "../../../../src/data/hassio/common";
|
||||
import {
|
||||
fetchHassioSnapshotInfo,
|
||||
HassioSnapshotDetail,
|
||||
} from "../../../../src/data/hassio/snapshot";
|
||||
import {
|
||||
showAlertDialog,
|
||||
showConfirmationDialog,
|
||||
} from "../../../../src/dialogs/generic/show-dialog-box";
|
||||
import { HassDialog } from "../../../../src/dialogs/make-dialog-manager";
|
||||
import { haStyle, haStyleDialog } from "../../../../src/resources/styles";
|
||||
import { HomeAssistant } from "../../../../src/types";
|
||||
import "../../components/supervisor-snapshot-content";
|
||||
import type { SupervisorSnapshotContent } from "../../components/supervisor-snapshot-content";
|
||||
import { HassioSnapshotDialogParams } from "./show-dialog-hassio-snapshot";
|
||||
|
||||
@customElement("dialog-hassio-snapshot")
|
||||
class HassioSnapshotDialog
|
||||
extends LitElement
|
||||
implements HassDialog<HassioSnapshotDialogParams> {
|
||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||
|
||||
@state() private _error?: string;
|
||||
|
||||
@state() private _snapshot?: HassioSnapshotDetail;
|
||||
|
||||
@state() private _dialogParams?: HassioSnapshotDialogParams;
|
||||
|
||||
@state() private _restoringSnapshot = false;
|
||||
|
||||
@query("supervisor-snapshot-content")
|
||||
private _snapshotContent!: SupervisorSnapshotContent;
|
||||
|
||||
public async showDialog(params: HassioSnapshotDialogParams) {
|
||||
this._snapshot = await fetchHassioSnapshotInfo(this.hass, params.slug);
|
||||
this._dialogParams = params;
|
||||
this._restoringSnapshot = false;
|
||||
}
|
||||
|
||||
public closeDialog() {
|
||||
this._snapshot = undefined;
|
||||
this._dialogParams = undefined;
|
||||
this._restoringSnapshot = false;
|
||||
this._error = undefined;
|
||||
fireEvent(this, "dialog-closed", { dialog: this.localName });
|
||||
}
|
||||
|
||||
protected render(): TemplateResult {
|
||||
if (!this._dialogParams || !this._snapshot) {
|
||||
return html``;
|
||||
}
|
||||
return html`
|
||||
<ha-dialog
|
||||
open
|
||||
scrimClickAction
|
||||
@closed=${this.closeDialog}
|
||||
.heading=${createCloseHeading(this.hass, this._computeName)}
|
||||
>
|
||||
${this._restoringSnapshot
|
||||
? html` <ha-circular-progress active></ha-circular-progress>`
|
||||
: html`<supervisor-snapshot-content
|
||||
.hass=${this.hass}
|
||||
.supervisor=${this._dialogParams.supervisor}
|
||||
.snapshot=${this._snapshot}
|
||||
>
|
||||
</supervisor-snapshot-content>`}
|
||||
${this._error ? html`<p class="error">Error: ${this._error}</p>` : ""}
|
||||
|
||||
<mwc-button
|
||||
.disabled=${this._restoringSnapshot}
|
||||
slot="secondaryAction"
|
||||
@click=${this._restoreClicked}
|
||||
>
|
||||
Restore
|
||||
</mwc-button>
|
||||
|
||||
<ha-button-menu
|
||||
fixed
|
||||
slot="primaryAction"
|
||||
@action=${this._handleMenuAction}
|
||||
@closed=${(ev: Event) => ev.stopPropagation()}
|
||||
>
|
||||
<mwc-icon-button slot="trigger" alt="menu">
|
||||
<ha-svg-icon .path=${mdiDotsVertical}></ha-svg-icon>
|
||||
</mwc-icon-button>
|
||||
<mwc-list-item>Download Snapshot</mwc-list-item>
|
||||
<mwc-list-item class="error">Delete Snapshot</mwc-list-item>
|
||||
</ha-button-menu>
|
||||
</ha-dialog>
|
||||
`;
|
||||
}
|
||||
|
||||
static get styles(): CSSResultGroup {
|
||||
return [
|
||||
haStyle,
|
||||
haStyleDialog,
|
||||
css`
|
||||
ha-svg-icon {
|
||||
color: var(--primary-text-color);
|
||||
}
|
||||
ha-circular-progress {
|
||||
display: block;
|
||||
text-align: center;
|
||||
}
|
||||
`,
|
||||
];
|
||||
}
|
||||
|
||||
private _handleMenuAction(ev: CustomEvent<ActionDetail>) {
|
||||
switch (ev.detail.index) {
|
||||
case 0:
|
||||
this._downloadClicked();
|
||||
break;
|
||||
case 1:
|
||||
this._deleteClicked();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private async _restoreClicked() {
|
||||
const snapshotDetails = this._snapshotContent.snapshotDetails();
|
||||
this._restoringSnapshot = true;
|
||||
if (this._snapshotContent.snapshotType === "full") {
|
||||
await this._fullRestoreClicked(snapshotDetails);
|
||||
} else {
|
||||
await this._partialRestoreClicked(snapshotDetails);
|
||||
}
|
||||
this._restoringSnapshot = false;
|
||||
}
|
||||
|
||||
private async _partialRestoreClicked(snapshotDetails) {
|
||||
if (
|
||||
this._dialogParams?.supervisor !== undefined &&
|
||||
this._dialogParams?.supervisor.info.state !== "running"
|
||||
) {
|
||||
await showAlertDialog(this, {
|
||||
title: "Could not restore snapshot",
|
||||
text: `Restoring a snapshot is not possible right now because the system is in ${this._dialogParams?.supervisor.info.state} state.`,
|
||||
});
|
||||
return;
|
||||
}
|
||||
if (
|
||||
!(await showConfirmationDialog(this, {
|
||||
title: "Are you sure you want partially to restore this snapshot?",
|
||||
confirmText: "restore",
|
||||
dismissText: "cancel",
|
||||
}))
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this._dialogParams?.onboarding) {
|
||||
this.hass
|
||||
.callApi(
|
||||
"POST",
|
||||
|
||||
`hassio/snapshots/${this._snapshot!.slug}/restore/partial`,
|
||||
snapshotDetails
|
||||
)
|
||||
.then(
|
||||
() => {
|
||||
this.closeDialog();
|
||||
},
|
||||
(error) => {
|
||||
this._error = error.body.message;
|
||||
}
|
||||
);
|
||||
} else {
|
||||
fireEvent(this, "restoring");
|
||||
fetch(`/api/hassio/snapshots/${this._snapshot!.slug}/restore/partial`, {
|
||||
method: "POST",
|
||||
body: JSON.stringify(snapshotDetails),
|
||||
});
|
||||
this.closeDialog();
|
||||
}
|
||||
}
|
||||
|
||||
private async _fullRestoreClicked(snapshotDetails) {
|
||||
if (
|
||||
this._dialogParams?.supervisor !== undefined &&
|
||||
this._dialogParams?.supervisor.info.state !== "running"
|
||||
) {
|
||||
await showAlertDialog(this, {
|
||||
title: "Could not restore snapshot",
|
||||
text: `Restoring a snapshot is not possible right now because the system is in ${this._dialogParams?.supervisor.info.state} state.`,
|
||||
});
|
||||
return;
|
||||
}
|
||||
if (
|
||||
!(await showConfirmationDialog(this, {
|
||||
title:
|
||||
"Are you sure you want to wipe your system and restore this snapshot?",
|
||||
confirmText: "restore",
|
||||
dismissText: "cancel",
|
||||
}))
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this._dialogParams?.onboarding) {
|
||||
this.hass
|
||||
.callApi(
|
||||
"POST",
|
||||
`hassio/snapshots/${this._snapshot!.slug}/restore/full`,
|
||||
snapshotDetails
|
||||
)
|
||||
.then(
|
||||
() => {
|
||||
this.closeDialog();
|
||||
},
|
||||
(error) => {
|
||||
this._error = error.body.message;
|
||||
}
|
||||
);
|
||||
} else {
|
||||
fireEvent(this, "restoring");
|
||||
fetch(`/api/hassio/snapshots/${this._snapshot!.slug}/restore/full`, {
|
||||
method: "POST",
|
||||
body: JSON.stringify(snapshotDetails),
|
||||
});
|
||||
this.closeDialog();
|
||||
}
|
||||
}
|
||||
|
||||
private async _deleteClicked() {
|
||||
if (
|
||||
!(await showConfirmationDialog(this, {
|
||||
title: "Are you sure you want to delete this snapshot?",
|
||||
confirmText: "delete",
|
||||
dismissText: "cancel",
|
||||
}))
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.hass
|
||||
|
||||
.callApi("POST", `hassio/snapshots/${this._snapshot!.slug}/remove`)
|
||||
.then(
|
||||
() => {
|
||||
if (this._dialogParams!.onDelete) {
|
||||
this._dialogParams!.onDelete();
|
||||
}
|
||||
this.closeDialog();
|
||||
},
|
||||
(error) => {
|
||||
this._error = error.body.message;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
private async _downloadClicked() {
|
||||
let signedPath: { path: string };
|
||||
try {
|
||||
signedPath = await getSignedPath(
|
||||
this.hass,
|
||||
`/api/hassio/snapshots/${this._snapshot!.slug}/download`
|
||||
);
|
||||
} catch (err) {
|
||||
await showAlertDialog(this, {
|
||||
text: extractApiErrorMessage(err),
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (window.location.href.includes("ui.nabu.casa")) {
|
||||
const confirm = await showConfirmationDialog(this, {
|
||||
title: "Potential slow download",
|
||||
text:
|
||||
"Downloading snapshots over the Nabu Casa URL will take some time, it is recomended to use your local URL instead, do you want to continue?",
|
||||
confirmText: "continue",
|
||||
dismissText: "cancel",
|
||||
});
|
||||
if (!confirm) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const a = document.createElement("a");
|
||||
a.href = signedPath.path;
|
||||
a.download = `home_assistant_snapshot_${slugify(this._computeName)}.tar`;
|
||||
this.shadowRoot!.appendChild(a);
|
||||
a.click();
|
||||
this.shadowRoot!.removeChild(a);
|
||||
}
|
||||
|
||||
private get _computeName() {
|
||||
return this._snapshot
|
||||
? this._snapshot.name || this._snapshot.slug
|
||||
: "Unnamed snapshot";
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"dialog-hassio-snapshot": HassioSnapshotDialog;
|
||||
}
|
||||
}
|
@@ -1,18 +1,18 @@
|
||||
import { fireEvent } from "../../../../src/common/dom/fire_event";
|
||||
import { Supervisor } from "../../../../src/data/supervisor/supervisor";
|
||||
|
||||
export interface HassioCreateBackupDialogParams {
|
||||
export interface HassioCreateSnapshotDialogParams {
|
||||
supervisor: Supervisor;
|
||||
onCreate: () => void;
|
||||
}
|
||||
|
||||
export const showHassioCreateBackupDialog = (
|
||||
export const showHassioCreateSnapshotDialog = (
|
||||
element: HTMLElement,
|
||||
dialogParams: HassioCreateBackupDialogParams
|
||||
dialogParams: HassioCreateSnapshotDialogParams
|
||||
): void => {
|
||||
fireEvent(element, "show-dialog", {
|
||||
dialogTag: "dialog-hassio-create-backup",
|
||||
dialogImport: () => import("./dialog-hassio-create-backup"),
|
||||
dialogTag: "dialog-hassio-create-snapshot",
|
||||
dialogImport: () => import("./dialog-hassio-create-snapshot"),
|
||||
dialogParams,
|
||||
});
|
||||
};
|
@@ -1,22 +1,20 @@
|
||||
import { fireEvent } from "../../../../src/common/dom/fire_event";
|
||||
import { LocalizeFunc } from "../../../../src/common/translations/localize";
|
||||
import { Supervisor } from "../../../../src/data/supervisor/supervisor";
|
||||
|
||||
export interface HassioBackupDialogParams {
|
||||
export interface HassioSnapshotDialogParams {
|
||||
slug: string;
|
||||
onDelete?: () => void;
|
||||
onboarding?: boolean;
|
||||
supervisor?: Supervisor;
|
||||
localize?: LocalizeFunc;
|
||||
}
|
||||
|
||||
export const showHassioBackupDialog = (
|
||||
export const showHassioSnapshotDialog = (
|
||||
element: HTMLElement,
|
||||
dialogParams: HassioBackupDialogParams
|
||||
dialogParams: HassioSnapshotDialogParams
|
||||
): void => {
|
||||
fireEvent(element, "show-dialog", {
|
||||
dialogTag: "dialog-hassio-backup",
|
||||
dialogImport: () => import("./dialog-hassio-backup"),
|
||||
dialogTag: "dialog-hassio-snapshot",
|
||||
dialogImport: () => import("./dialog-hassio-snapshot"),
|
||||
dialogParams,
|
||||
});
|
||||
};
|
19
hassio/src/dialogs/snapshot/show-dialog-snapshot-upload.ts
Normal file
19
hassio/src/dialogs/snapshot/show-dialog-snapshot-upload.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
import { fireEvent } from "../../../../src/common/dom/fire_event";
|
||||
import "./dialog-hassio-snapshot-upload";
|
||||
|
||||
export interface HassioSnapshotUploadDialogParams {
|
||||
showSnapshot: (slug: string) => void;
|
||||
reloadSnapshot?: () => Promise<void>;
|
||||
onboarding?: boolean;
|
||||
}
|
||||
|
||||
export const showSnapshotUploadDialog = (
|
||||
element: HTMLElement,
|
||||
dialogParams: HassioSnapshotUploadDialogParams
|
||||
): void => {
|
||||
fireEvent(element, "show-dialog", {
|
||||
dialogTag: "dialog-hassio-snapshot-upload",
|
||||
dialogImport: () => import("./dialog-hassio-snapshot-upload"),
|
||||
dialogParams,
|
||||
});
|
||||
};
|
@@ -26,7 +26,7 @@ export const suggestAddonRestart = async (
|
||||
if (confirmed) {
|
||||
try {
|
||||
await restartHassioAddon(hass, addon.slug);
|
||||
} catch (err: any) {
|
||||
} catch (err) {
|
||||
showAlertDialog(element, {
|
||||
title: supervisor.localize(
|
||||
"common.failed_to_restart_name",
|
||||
|
@@ -2,7 +2,6 @@ import "@material/mwc-button/mwc-button";
|
||||
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
||||
import { customElement, state } from "lit/decorators";
|
||||
import { fireEvent } from "../../../../src/common/dom/fire_event";
|
||||
import "../../../../src/components/ha-alert";
|
||||
import "../../../../src/components/ha-circular-progress";
|
||||
import "../../../../src/components/ha-dialog";
|
||||
import "../../../../src/components/ha-settings-row";
|
||||
@@ -12,7 +11,7 @@ import {
|
||||
extractApiErrorMessage,
|
||||
ignoreSupervisorError,
|
||||
} from "../../../../src/data/hassio/common";
|
||||
import { createHassioPartialBackup } from "../../../../src/data/hassio/backup";
|
||||
import { createHassioPartialSnapshot } from "../../../../src/data/hassio/snapshot";
|
||||
import { haStyle, haStyleDialog } from "../../../../src/resources/styles";
|
||||
import type { HomeAssistant } from "../../../../src/types";
|
||||
import { SupervisorDialogSupervisorUpdateParams } from "./show-dialog-update";
|
||||
@@ -23,9 +22,9 @@ class DialogSupervisorUpdate extends LitElement {
|
||||
|
||||
@state() private _opened = false;
|
||||
|
||||
@state() private _createBackup = true;
|
||||
@state() private _createSnapshot = true;
|
||||
|
||||
@state() private _action: "backup" | "update" | null = null;
|
||||
@state() private _action: "snapshot" | "update" | null = null;
|
||||
|
||||
@state() private _error?: string;
|
||||
|
||||
@@ -42,7 +41,7 @@ class DialogSupervisorUpdate extends LitElement {
|
||||
|
||||
public closeDialog(): void {
|
||||
this._action = null;
|
||||
this._createBackup = true;
|
||||
this._createSnapshot = true;
|
||||
this._error = undefined;
|
||||
this._dialogParams = undefined;
|
||||
fireEvent(this, "dialog-closed", { dialog: this.localName });
|
||||
@@ -50,9 +49,9 @@ class DialogSupervisorUpdate extends LitElement {
|
||||
|
||||
public focus(): void {
|
||||
this.updateComplete.then(() =>
|
||||
(
|
||||
this.shadowRoot?.querySelector("[dialogInitialFocus]") as HTMLElement
|
||||
)?.focus()
|
||||
(this.shadowRoot?.querySelector(
|
||||
"[dialogInitialFocus]"
|
||||
) as HTMLElement)?.focus()
|
||||
);
|
||||
}
|
||||
|
||||
@@ -85,20 +84,20 @@ class DialogSupervisorUpdate extends LitElement {
|
||||
<ha-settings-row>
|
||||
<span slot="heading">
|
||||
${this._dialogParams.supervisor.localize(
|
||||
"dialog.update.backup"
|
||||
"dialog.update.snapshot"
|
||||
)}
|
||||
</span>
|
||||
<span slot="description">
|
||||
${this._dialogParams.supervisor.localize(
|
||||
"dialog.update.create_backup",
|
||||
"dialog.update.create_snapshot",
|
||||
"name",
|
||||
this._dialogParams.name
|
||||
)}
|
||||
</span>
|
||||
<ha-switch
|
||||
.checked=${this._createBackup}
|
||||
.checked=${this._createSnapshot}
|
||||
haptic
|
||||
@click=${this._toggleBackup}
|
||||
@click=${this._toggleSnapshot}
|
||||
>
|
||||
</ha-switch>
|
||||
</ha-settings-row>
|
||||
@@ -124,31 +123,29 @@ class DialogSupervisorUpdate extends LitElement {
|
||||
this._dialogParams.version
|
||||
)
|
||||
: this._dialogParams.supervisor.localize(
|
||||
"dialog.update.creating_backup",
|
||||
"dialog.update.snapshotting",
|
||||
"name",
|
||||
this._dialogParams.name
|
||||
)}
|
||||
</p>`}
|
||||
${this._error
|
||||
? html`<ha-alert alert-type="error">${this._error}</ha-alert>`
|
||||
: ""}
|
||||
${this._error ? html`<p class="error">${this._error}</p>` : ""}
|
||||
</ha-dialog>
|
||||
`;
|
||||
}
|
||||
|
||||
private _toggleBackup() {
|
||||
this._createBackup = !this._createBackup;
|
||||
private _toggleSnapshot() {
|
||||
this._createSnapshot = !this._createSnapshot;
|
||||
}
|
||||
|
||||
private async _update() {
|
||||
if (this._createBackup) {
|
||||
this._action = "backup";
|
||||
if (this._createSnapshot) {
|
||||
this._action = "snapshot";
|
||||
try {
|
||||
await createHassioPartialBackup(
|
||||
await createHassioPartialSnapshot(
|
||||
this.hass,
|
||||
this._dialogParams!.backupParams
|
||||
this._dialogParams!.snapshotParams
|
||||
);
|
||||
} catch (err: any) {
|
||||
} catch (err) {
|
||||
this._error = extractApiErrorMessage(err);
|
||||
this._action = null;
|
||||
return;
|
||||
@@ -158,11 +155,11 @@ class DialogSupervisorUpdate extends LitElement {
|
||||
this._action = "update";
|
||||
try {
|
||||
await this._dialogParams!.updateHandler!();
|
||||
} catch (err: any) {
|
||||
} catch (err) {
|
||||
if (this.hass.connection.connected && !ignoreSupervisorError(err)) {
|
||||
this._error = extractApiErrorMessage(err);
|
||||
this._action = null;
|
||||
}
|
||||
this._action = null;
|
||||
return;
|
||||
}
|
||||
|
||||
|
@@ -5,7 +5,7 @@ export interface SupervisorDialogSupervisorUpdateParams {
|
||||
supervisor: Supervisor;
|
||||
name: string;
|
||||
version: string;
|
||||
backupParams: any;
|
||||
snapshotParams: any;
|
||||
updateHandler: () => Promise<void>;
|
||||
}
|
||||
|
||||
|
@@ -121,7 +121,7 @@ export class HassioMain extends SupervisorBaseElement {
|
||||
}
|
||||
} else {
|
||||
themeName =
|
||||
(this.hass.selectedTheme as unknown as string) ||
|
||||
((this.hass.selectedTheme as unknown) as string) ||
|
||||
this.hass.themes.default_theme;
|
||||
}
|
||||
|
||||
|
@@ -1,19 +1,19 @@
|
||||
import { sanitizeUrl } from "@braintree/sanitize-url";
|
||||
import { html, LitElement, TemplateResult } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { navigate } from "../../src/common/navigate";
|
||||
import { sanitizeUrl } from "@braintree/sanitize-url";
|
||||
import {
|
||||
createSearchParam,
|
||||
extractSearchParamsObject,
|
||||
} from "../../src/common/url/search-params";
|
||||
import { Supervisor } from "../../src/data/supervisor/supervisor";
|
||||
import "../../src/layouts/hass-error-screen";
|
||||
import {
|
||||
ParamType,
|
||||
Redirect,
|
||||
Redirects,
|
||||
} from "../../src/panels/my/ha-panel-my";
|
||||
import { navigate } from "../../src/common/navigate";
|
||||
import { HomeAssistant, Route } from "../../src/types";
|
||||
import { Supervisor } from "../../src/data/supervisor/supervisor";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
|
||||
const REDIRECTS: Redirects = {
|
||||
supervisor: {
|
||||
@@ -26,10 +26,7 @@ const REDIRECTS: Redirects = {
|
||||
redirect: "/hassio/system",
|
||||
},
|
||||
supervisor_snapshots: {
|
||||
redirect: "/hassio/backups",
|
||||
},
|
||||
supervisor_backups: {
|
||||
redirect: "/hassio/backups",
|
||||
redirect: "/hassio/snapshots",
|
||||
},
|
||||
supervisor_store: {
|
||||
redirect: "/hassio/store",
|
||||
@@ -87,7 +84,7 @@ class HassioMyRedirect extends LitElement {
|
||||
let url: string;
|
||||
try {
|
||||
url = this._createRedirectUrl(redirect);
|
||||
} catch (err: any) {
|
||||
} catch (err) {
|
||||
this._error = this.supervisor.localize("my.error");
|
||||
return;
|
||||
}
|
||||
|
@@ -9,7 +9,7 @@ import "./addon-store/hassio-addon-store";
|
||||
// Don't codesplit it, that way the dashboard always loads fast.
|
||||
import "./dashboard/hassio-dashboard";
|
||||
// Don't codesplit the others, because it breaks the UI when pushed to a Pi
|
||||
import "./backups/hassio-backups";
|
||||
import "./snapshots/hassio-snapshots";
|
||||
import "./system/hassio-system";
|
||||
|
||||
@customElement("hassio-panel-router")
|
||||
@@ -23,8 +23,6 @@ class HassioPanelRouter extends HassRouterPage {
|
||||
@property({ type: Boolean }) public narrow!: boolean;
|
||||
|
||||
protected routerOptions: RouterOptions = {
|
||||
beforeRender: (page: string) =>
|
||||
page === "snapshots" ? "backups" : undefined,
|
||||
routes: {
|
||||
dashboard: {
|
||||
tag: "hassio-dashboard",
|
||||
@@ -32,8 +30,8 @@ class HassioPanelRouter extends HassRouterPage {
|
||||
store: {
|
||||
tag: "hassio-addon-store",
|
||||
},
|
||||
backups: {
|
||||
tag: "hassio-backups",
|
||||
snapshots: {
|
||||
tag: "hassio-snapshots",
|
||||
},
|
||||
system: {
|
||||
tag: "hassio-system",
|
||||
|
@@ -23,8 +23,6 @@ class HassioRouter extends HassRouterPage {
|
||||
protected routerOptions: RouterOptions = {
|
||||
// Hass.io has a page with tabs, so we route all non-matching routes to it.
|
||||
defaultPage: "dashboard",
|
||||
beforeRender: (page: string) =>
|
||||
page === "snapshots" ? "backups" : undefined,
|
||||
initialLoad: () => this._redirectIngress(),
|
||||
showLoading: true,
|
||||
routes: {
|
||||
@@ -32,7 +30,7 @@ class HassioRouter extends HassRouterPage {
|
||||
tag: "hassio-panel",
|
||||
cache: true,
|
||||
},
|
||||
backups: "dashboard",
|
||||
snapshots: "dashboard",
|
||||
store: "dashboard",
|
||||
system: "dashboard",
|
||||
addon: {
|
||||
|
@@ -13,8 +13,8 @@ export const supervisorTabs: PageNavigation[] = [
|
||||
iconPath: mdiStore,
|
||||
},
|
||||
{
|
||||
translationKey: "panel.backups",
|
||||
path: `/hassio/backups`,
|
||||
translationKey: "panel.snapshots",
|
||||
path: `/hassio/snapshots`,
|
||||
iconPath: mdiBackupRestore,
|
||||
},
|
||||
{
|
||||
|
@@ -91,29 +91,22 @@ class HassioIngressView extends LitElement {
|
||||
if (requestedAddon) {
|
||||
try {
|
||||
addonInfo = await fetchHassioAddonInfo(this.hass, requestedAddon);
|
||||
} catch (err: any) {
|
||||
} catch (err) {
|
||||
await showAlertDialog(this, {
|
||||
text: extractApiErrorMessage(err),
|
||||
title: requestedAddon,
|
||||
});
|
||||
await nextRender();
|
||||
navigate("/hassio/store", { replace: true });
|
||||
history.back();
|
||||
return;
|
||||
}
|
||||
if (!addonInfo.version) {
|
||||
await showAlertDialog(this, {
|
||||
text: this.supervisor.localize("my.error_addon_not_installed"),
|
||||
title: addonInfo.name,
|
||||
});
|
||||
await nextRender();
|
||||
navigate(`/hassio/addon/${addonInfo.slug}/info`, { replace: true });
|
||||
} else if (!addonInfo.ingress) {
|
||||
if (!addonInfo.ingress) {
|
||||
await showAlertDialog(this, {
|
||||
text: this.supervisor.localize("my.error_addon_no_ingress"),
|
||||
title: addonInfo.name,
|
||||
});
|
||||
await nextRender();
|
||||
navigate(`/hassio/addon/${addonInfo.slug}/info`, { replace: true });
|
||||
history.back();
|
||||
} else {
|
||||
navigate(`/hassio/ingress/${addonInfo.slug}`, { replace: true });
|
||||
}
|
||||
@@ -145,7 +138,7 @@ class HassioIngressView extends LitElement {
|
||||
|
||||
try {
|
||||
addon = await fetchHassioAddonInfo(this.hass, addonSlug);
|
||||
} catch (err: any) {
|
||||
} catch (err) {
|
||||
await showAlertDialog(this, {
|
||||
text: "Unable to fetch add-on info to start Ingress",
|
||||
title: "Supervisor",
|
||||
@@ -179,7 +172,7 @@ class HassioIngressView extends LitElement {
|
||||
|
||||
try {
|
||||
session = await createSessionPromise;
|
||||
} catch (err: any) {
|
||||
} catch (err) {
|
||||
await showAlertDialog(this, {
|
||||
text: "Unable to create an Ingress session",
|
||||
title: addon.name,
|
||||
@@ -195,7 +188,7 @@ class HassioIngressView extends LitElement {
|
||||
this._sessionKeepAlive = window.setInterval(async () => {
|
||||
try {
|
||||
await validateHassioSession(this.hass, session);
|
||||
} catch (err: any) {
|
||||
} catch (err) {
|
||||
session = await createHassioSession(this.hass);
|
||||
}
|
||||
}, 60000);
|
||||
|
@@ -14,7 +14,7 @@ import { customElement, property, query, state } from "lit/decorators";
|
||||
import { classMap } from "lit/directives/class-map";
|
||||
import memoizeOne from "memoize-one";
|
||||
import { atLeastVersion } from "../../../src/common/config/version";
|
||||
import { relativeTime } from "../../../src/common/datetime/relative_time";
|
||||
import relativeTime from "../../../src/common/datetime/relative_time";
|
||||
import { HASSDomEvent } from "../../../src/common/dom/fire_event";
|
||||
import {
|
||||
DataTableColumnContainer,
|
||||
@@ -25,12 +25,12 @@ import "../../../src/components/ha-button-menu";
|
||||
import "../../../src/components/ha-fab";
|
||||
import { extractApiErrorMessage } from "../../../src/data/hassio/common";
|
||||
import {
|
||||
fetchHassioBackups,
|
||||
fetchHassioSnapshots,
|
||||
friendlyFolderName,
|
||||
HassioBackup,
|
||||
reloadHassioBackups,
|
||||
removeBackup,
|
||||
} from "../../../src/data/hassio/backup";
|
||||
HassioSnapshot,
|
||||
reloadHassioSnapshots,
|
||||
removeSnapshot,
|
||||
} from "../../../src/data/hassio/snapshot";
|
||||
import { Supervisor } from "../../../src/data/supervisor/supervisor";
|
||||
import {
|
||||
showAlertDialog,
|
||||
@@ -40,14 +40,14 @@ import "../../../src/layouts/hass-tabs-subpage-data-table";
|
||||
import type { HaTabsSubpageDataTable } from "../../../src/layouts/hass-tabs-subpage-data-table";
|
||||
import { haStyle } from "../../../src/resources/styles";
|
||||
import { HomeAssistant, Route } from "../../../src/types";
|
||||
import { showHassioCreateBackupDialog } from "../dialogs/backup/show-dialog-hassio-create-backup";
|
||||
import { showHassioBackupDialog } from "../dialogs/backup/show-dialog-hassio-backup";
|
||||
import { showBackupUploadDialog } from "../dialogs/backup/show-dialog-backup-upload";
|
||||
import { showHassioCreateSnapshotDialog } from "../dialogs/snapshot/show-dialog-hassio-create-snapshot";
|
||||
import { showHassioSnapshotDialog } from "../dialogs/snapshot/show-dialog-hassio-snapshot";
|
||||
import { showSnapshotUploadDialog } from "../dialogs/snapshot/show-dialog-snapshot-upload";
|
||||
import { supervisorTabs } from "../hassio-tabs";
|
||||
import { hassioStyle } from "../resources/hassio-style";
|
||||
|
||||
@customElement("hassio-backups")
|
||||
export class HassioBackups extends LitElement {
|
||||
@customElement("hassio-snapshots")
|
||||
export class HassioSnapshots extends LitElement {
|
||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||
|
||||
@property({ attribute: false }) public supervisor!: Supervisor;
|
||||
@@ -58,9 +58,9 @@ export class HassioBackups extends LitElement {
|
||||
|
||||
@property({ type: Boolean }) public isWide!: boolean;
|
||||
|
||||
@state() private _selectedBackups: string[] = [];
|
||||
@state() private _selectedSnapshots: string[] = [];
|
||||
|
||||
@state() private _backups?: HassioBackup[] = [];
|
||||
@state() private _snapshots?: HassioSnapshot[] = [];
|
||||
|
||||
@query("hass-tabs-subpage-data-table", true)
|
||||
private _dataTable!: HaTabsSubpageDataTable;
|
||||
@@ -75,26 +75,26 @@ export class HassioBackups extends LitElement {
|
||||
}
|
||||
|
||||
public async refreshData() {
|
||||
await reloadHassioBackups(this.hass);
|
||||
await this.fetchBackups();
|
||||
await reloadHassioSnapshots(this.hass);
|
||||
await this.fetchSnapshots();
|
||||
}
|
||||
|
||||
private _computeBackupContent = (backup: HassioBackup): string => {
|
||||
if (backup.type === "full") {
|
||||
return this.supervisor.localize("backup.full_backup");
|
||||
private _computeSnapshotContent = (snapshot: HassioSnapshot): string => {
|
||||
if (snapshot.type === "full") {
|
||||
return this.supervisor.localize("snapshot.full_snapshot");
|
||||
}
|
||||
const content: string[] = [];
|
||||
if (backup.content.homeassistant) {
|
||||
if (snapshot.content.homeassistant) {
|
||||
content.push("Home Assistant");
|
||||
}
|
||||
if (backup.content.folders.length !== 0) {
|
||||
for (const folder of backup.content.folders) {
|
||||
if (snapshot.content.folders.length !== 0) {
|
||||
for (const folder of snapshot.content.folders) {
|
||||
content.push(friendlyFolderName[folder] || folder);
|
||||
}
|
||||
}
|
||||
|
||||
if (backup.content.addons.length !== 0) {
|
||||
for (const addon of backup.content.addons) {
|
||||
if (snapshot.content.addons.length !== 0) {
|
||||
for (const addon of snapshot.content.addons) {
|
||||
content.push(
|
||||
this.supervisor.supervisor.addons.find(
|
||||
(entry) => entry.slug === addon
|
||||
@@ -117,23 +117,23 @@ export class HassioBackups extends LitElement {
|
||||
private _columns = memoizeOne(
|
||||
(narrow: boolean): DataTableColumnContainer => ({
|
||||
name: {
|
||||
title: this.supervisor?.localize("backup.name") || "",
|
||||
title: this.supervisor?.localize("snapshot.name") || "",
|
||||
sortable: true,
|
||||
filterable: true,
|
||||
grows: true,
|
||||
template: (entry: string, backup: any) =>
|
||||
html`${entry || backup.slug}
|
||||
<div class="secondary">${backup.secondary}</div>`,
|
||||
template: (entry: string, snapshot: any) =>
|
||||
html`${entry || snapshot.slug}
|
||||
<div class="secondary">${snapshot.secondary}</div>`,
|
||||
},
|
||||
date: {
|
||||
title: this.supervisor?.localize("backup.created") || "",
|
||||
title: this.supervisor?.localize("snapshot.created") || "",
|
||||
width: "15%",
|
||||
direction: "desc",
|
||||
hidden: narrow,
|
||||
filterable: true,
|
||||
sortable: true,
|
||||
template: (entry: string) =>
|
||||
relativeTime(new Date(entry), this.hass.locale),
|
||||
relativeTime(new Date(entry), this.hass.localize),
|
||||
},
|
||||
secondary: {
|
||||
title: "",
|
||||
@@ -143,10 +143,10 @@ export class HassioBackups extends LitElement {
|
||||
})
|
||||
);
|
||||
|
||||
private _backupData = memoizeOne((backups: HassioBackup[]) =>
|
||||
backups.map((backup) => ({
|
||||
...backup,
|
||||
secondary: this._computeBackupContent(backup),
|
||||
private _snapshotData = memoizeOne((snapshots: HassioSnapshot[]) =>
|
||||
snapshots.map((snapshot) => ({
|
||||
...snapshot,
|
||||
secondary: this._computeSnapshotContent(snapshot),
|
||||
}))
|
||||
);
|
||||
|
||||
@@ -160,11 +160,11 @@ export class HassioBackups extends LitElement {
|
||||
.hass=${this.hass}
|
||||
.localizeFunc=${this.supervisor.localize}
|
||||
.searchLabel=${this.supervisor.localize("search")}
|
||||
.noDataText=${this.supervisor.localize("backup.no_backups")}
|
||||
.noDataText=${this.supervisor.localize("snapshot.no_snapshots")}
|
||||
.narrow=${this.narrow}
|
||||
.route=${this.route}
|
||||
.columns=${this._columns(this.narrow)}
|
||||
.data=${this._backupData(this._backups || [])}
|
||||
.data=${this._snapshotData(this._snapshots || [])}
|
||||
id="slug"
|
||||
@row-click=${this._handleRowClicked}
|
||||
@selection-changed=${this._handleSelectionChanged}
|
||||
@@ -187,12 +187,12 @@ export class HassioBackups extends LitElement {
|
||||
</mwc-list-item>
|
||||
${atLeastVersion(this.hass.config.version, 0, 116)
|
||||
? html`<mwc-list-item>
|
||||
${this.supervisor?.localize("backup.upload_backup")}
|
||||
${this.supervisor?.localize("snapshot.upload_snapshot")}
|
||||
</mwc-list-item>`
|
||||
: ""}
|
||||
</ha-button-menu>
|
||||
|
||||
${this._selectedBackups.length
|
||||
${this._selectedSnapshots.length
|
||||
? html`<div
|
||||
class=${classMap({
|
||||
"header-toolbar": this.narrow,
|
||||
@@ -201,8 +201,8 @@ export class HassioBackups extends LitElement {
|
||||
slot="header"
|
||||
>
|
||||
<p class="selected-txt">
|
||||
${this.supervisor.localize("backup.selected", {
|
||||
number: this._selectedBackups.length,
|
||||
${this.supervisor.localize("snapshot.selected", {
|
||||
number: this._selectedSnapshots.length,
|
||||
})}
|
||||
</p>
|
||||
<div class="header-btns">
|
||||
@@ -212,7 +212,7 @@ export class HassioBackups extends LitElement {
|
||||
@click=${this._deleteSelected}
|
||||
class="warning"
|
||||
>
|
||||
${this.supervisor.localize("backup.delete_selected")}
|
||||
${this.supervisor.localize("snapshot.delete_selected")}
|
||||
</mwc-button>
|
||||
`
|
||||
: html`
|
||||
@@ -224,7 +224,7 @@ export class HassioBackups extends LitElement {
|
||||
<ha-svg-icon .path=${mdiDelete}></ha-svg-icon>
|
||||
</mwc-icon-button>
|
||||
<paper-tooltip animation-delay="0" for="delete-btn">
|
||||
${this.supervisor.localize("backup.delete_selected")}
|
||||
${this.supervisor.localize("snapshot.delete_selected")}
|
||||
</paper-tooltip>
|
||||
`}
|
||||
</div>
|
||||
@@ -233,8 +233,8 @@ export class HassioBackups extends LitElement {
|
||||
|
||||
<ha-fab
|
||||
slot="fab"
|
||||
@click=${this._createBackup}
|
||||
.label=${this.supervisor.localize("backup.create_backup")}
|
||||
@click=${this._createSnapshot}
|
||||
.label=${this.supervisor.localize("snapshot.create_snapshot")}
|
||||
extended
|
||||
>
|
||||
<ha-svg-icon slot="icon" .path=${mdiPlus}></ha-svg-icon>
|
||||
@@ -249,7 +249,7 @@ export class HassioBackups extends LitElement {
|
||||
this.refreshData();
|
||||
break;
|
||||
case 1:
|
||||
this._showUploadBackupDialog();
|
||||
this._showUploadSnapshotDialog();
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -257,33 +257,33 @@ export class HassioBackups extends LitElement {
|
||||
private _handleSelectionChanged(
|
||||
ev: HASSDomEvent<SelectionChangedEvent>
|
||||
): void {
|
||||
this._selectedBackups = ev.detail.value;
|
||||
this._selectedSnapshots = ev.detail.value;
|
||||
}
|
||||
|
||||
private _showUploadBackupDialog() {
|
||||
showBackupUploadDialog(this, {
|
||||
showBackup: (slug: string) =>
|
||||
showHassioBackupDialog(this, {
|
||||
private _showUploadSnapshotDialog() {
|
||||
showSnapshotUploadDialog(this, {
|
||||
showSnapshot: (slug: string) =>
|
||||
showHassioSnapshotDialog(this, {
|
||||
slug,
|
||||
supervisor: this.supervisor,
|
||||
onDelete: () => this.fetchBackups(),
|
||||
onDelete: () => this.fetchSnapshots(),
|
||||
}),
|
||||
reloadBackup: () => this.refreshData(),
|
||||
reloadSnapshot: () => this.refreshData(),
|
||||
});
|
||||
}
|
||||
|
||||
private async fetchBackups() {
|
||||
await reloadHassioBackups(this.hass);
|
||||
this._backups = await fetchHassioBackups(this.hass);
|
||||
private async fetchSnapshots() {
|
||||
await reloadHassioSnapshots(this.hass);
|
||||
this._snapshots = await fetchHassioSnapshots(this.hass);
|
||||
}
|
||||
|
||||
private async _deleteSelected() {
|
||||
const confirm = await showConfirmationDialog(this, {
|
||||
title: this.supervisor.localize("backup.delete_backup_title"),
|
||||
text: this.supervisor.localize("backup.delete_backup_text", {
|
||||
number: this._selectedBackups.length,
|
||||
title: this.supervisor.localize("snapshot.delete_snapshot_title"),
|
||||
text: this.supervisor.localize("snapshot.delete_snapshot_text", {
|
||||
number: this._selectedSnapshots.length,
|
||||
}),
|
||||
confirmText: this.supervisor.localize("backup.delete_backup_confirm"),
|
||||
confirmText: this.supervisor.localize("snapshot.delete_snapshot_confirm"),
|
||||
});
|
||||
|
||||
if (!confirm) {
|
||||
@@ -292,44 +292,44 @@ export class HassioBackups extends LitElement {
|
||||
|
||||
try {
|
||||
await Promise.all(
|
||||
this._selectedBackups.map((slug) => removeBackup(this.hass, slug))
|
||||
this._selectedSnapshots.map((slug) => removeSnapshot(this.hass, slug))
|
||||
);
|
||||
} catch (err: any) {
|
||||
} catch (err) {
|
||||
showAlertDialog(this, {
|
||||
title: this.supervisor.localize("backup.failed_to_delete"),
|
||||
title: this.supervisor.localize("snapshot.failed_to_delete"),
|
||||
text: extractApiErrorMessage(err),
|
||||
});
|
||||
return;
|
||||
}
|
||||
await reloadHassioBackups(this.hass);
|
||||
this._backups = await fetchHassioBackups(this.hass);
|
||||
await reloadHassioSnapshots(this.hass);
|
||||
this._snapshots = await fetchHassioSnapshots(this.hass);
|
||||
this._dataTable.clearSelection();
|
||||
}
|
||||
|
||||
private _handleRowClicked(ev: HASSDomEvent<RowClickedEvent>) {
|
||||
const slug = ev.detail.id;
|
||||
showHassioBackupDialog(this, {
|
||||
showHassioSnapshotDialog(this, {
|
||||
slug,
|
||||
supervisor: this.supervisor,
|
||||
onDelete: () => this.fetchBackups(),
|
||||
onDelete: () => this.fetchSnapshots(),
|
||||
});
|
||||
}
|
||||
|
||||
private _createBackup() {
|
||||
private _createSnapshot() {
|
||||
if (this.supervisor!.info.state !== "running") {
|
||||
showAlertDialog(this, {
|
||||
title: this.supervisor!.localize("backup.could_not_create"),
|
||||
title: this.supervisor!.localize("snapshot.could_not_create"),
|
||||
text: this.supervisor!.localize(
|
||||
"backup.create_blocked_not_running",
|
||||
"snapshot.create_blocked_not_running",
|
||||
"state",
|
||||
this.supervisor!.info.state
|
||||
),
|
||||
});
|
||||
return;
|
||||
}
|
||||
showHassioCreateBackupDialog(this, {
|
||||
showHassioCreateSnapshotDialog(this, {
|
||||
supervisor: this.supervisor!,
|
||||
onCreate: () => this.fetchBackups(),
|
||||
onCreate: () => this.fetchSnapshots(),
|
||||
});
|
||||
}
|
||||
|
||||
@@ -378,6 +378,6 @@ export class HassioBackups extends LitElement {
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"hassio-backups": HassioBackups;
|
||||
"hassio-snapshots": HassioSnapshots;
|
||||
}
|
||||
}
|
@@ -86,8 +86,10 @@ export class SupervisorBaseElement extends urlSyncMixin(
|
||||
const unsubs = Object.keys(this._unsubs);
|
||||
for (const collection of Object.keys(this._collections)) {
|
||||
if (!unsubs.includes(collection)) {
|
||||
this._unsubs[collection] = this._collections[collection].subscribe(
|
||||
(data) => this._updateSupervisor({ [collection]: data })
|
||||
this._unsubs[collection] = this._collections[
|
||||
collection
|
||||
].subscribe((data) =>
|
||||
this._updateSupervisor({ [collection]: data })
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@@ -144,7 +144,7 @@ class HassioCoreInfo extends LitElement {
|
||||
|
||||
try {
|
||||
await restartCore(this.hass);
|
||||
} catch (err: any) {
|
||||
} catch (err) {
|
||||
if (this.hass.connection.connected) {
|
||||
showAlertDialog(this, {
|
||||
title: this.supervisor.localize(
|
||||
@@ -165,7 +165,7 @@ class HassioCoreInfo extends LitElement {
|
||||
supervisor: this.supervisor,
|
||||
name: "Home Assistant Core",
|
||||
version: this.supervisor.core.version_latest,
|
||||
backupParams: {
|
||||
snapshotParams: {
|
||||
name: `core_${this.supervisor.core.version}`,
|
||||
folders: ["homeassistant"],
|
||||
homeassistant: true,
|
||||
|
@@ -1,6 +1,8 @@
|
||||
import "@material/mwc-button";
|
||||
import { ActionDetail } from "@material/mwc-list/mwc-list-foundation";
|
||||
import "@material/mwc-list/mwc-list-item";
|
||||
import { mdiDotsVertical } from "@mdi/js";
|
||||
import { dump } from "js-yaml";
|
||||
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
||||
import { customElement, property } from "lit/decorators";
|
||||
import memoizeOne from "memoize-one";
|
||||
@@ -39,8 +41,7 @@ import {
|
||||
roundWithOneDecimal,
|
||||
} from "../../../src/util/calculate";
|
||||
import "../components/supervisor-metric";
|
||||
import { showHassioDatadiskDialog } from "../dialogs/datadisk/show-dialog-hassio-datadisk";
|
||||
import { showHassioHardwareDialog } from "../dialogs/hardware/show-dialog-hassio-hardware";
|
||||
import { showHassioMarkdownDialog } from "../dialogs/markdown/show-dialog-hassio-markdown";
|
||||
import { showNetworkDialog } from "../dialogs/network/show-dialog-network";
|
||||
import { hassioStyle } from "../resources/hassio-style";
|
||||
|
||||
@@ -113,7 +114,7 @@ class HassioHostInfo extends LitElement {
|
||||
`
|
||||
: ""}
|
||||
</ha-settings-row>
|
||||
${!this.supervisor.host.features.includes("haos")
|
||||
${!this.supervisor.host.features.includes("hassos")
|
||||
? html`<ha-settings-row>
|
||||
<span slot="heading">
|
||||
${this.supervisor.localize("system.host.docker_version")}
|
||||
@@ -180,38 +181,20 @@ class HassioHostInfo extends LitElement {
|
||||
`
|
||||
: ""}
|
||||
|
||||
<ha-button-menu corner="BOTTOM_START">
|
||||
<ha-button-menu
|
||||
corner="BOTTOM_START"
|
||||
@action=${this._handleMenuAction}
|
||||
>
|
||||
<mwc-icon-button slot="trigger">
|
||||
<ha-svg-icon .path=${mdiDotsVertical}></ha-svg-icon>
|
||||
</mwc-icon-button>
|
||||
<mwc-list-item
|
||||
.action=${"hardware"}
|
||||
@click=${this._handleMenuAction}
|
||||
>
|
||||
<mwc-list-item>
|
||||
${this.supervisor.localize("system.host.hardware")}
|
||||
</mwc-list-item>
|
||||
${this.supervisor.host.features.includes("haos")
|
||||
? html`
|
||||
<mwc-list-item
|
||||
.action=${"import_from_usb"}
|
||||
@click=${this._handleMenuAction}
|
||||
>
|
||||
${this.supervisor.localize("system.host.import_from_usb")}
|
||||
</mwc-list-item>
|
||||
${this.supervisor.host.features.includes("os_agent") &&
|
||||
atLeastVersion(this.supervisor.host.agent_version, 1, 2, 0)
|
||||
? html`
|
||||
<mwc-list-item
|
||||
.action=${"move_datadisk"}
|
||||
@click=${this._handleMenuAction}
|
||||
>
|
||||
${this.supervisor.localize(
|
||||
"system.host.move_datadisk"
|
||||
)}
|
||||
</mwc-list-item>
|
||||
`
|
||||
: ""}
|
||||
`
|
||||
${this.supervisor.host.features.includes("hassos")
|
||||
? html`<mwc-list-item>
|
||||
${this.supervisor.localize("system.host.import_from_usb")}
|
||||
</mwc-list-item>`
|
||||
: ""}
|
||||
</ha-button-menu>
|
||||
</div>
|
||||
@@ -234,40 +217,32 @@ class HassioHostInfo extends LitElement {
|
||||
return network_info.interfaces.find((a) => a.primary)?.ipv4?.address![0];
|
||||
});
|
||||
|
||||
private async _handleMenuAction(ev) {
|
||||
switch ((ev.target as any).action) {
|
||||
case "hardware":
|
||||
private async _handleMenuAction(ev: CustomEvent<ActionDetail>) {
|
||||
switch (ev.detail.index) {
|
||||
case 0:
|
||||
await this._showHardware();
|
||||
break;
|
||||
case "import_from_usb":
|
||||
case 1:
|
||||
await this._importFromUSB();
|
||||
break;
|
||||
case "move_datadisk":
|
||||
await this._moveDatadisk();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private _moveDatadisk(): void {
|
||||
showHassioDatadiskDialog(this, {
|
||||
supervisor: this.supervisor,
|
||||
});
|
||||
}
|
||||
|
||||
private async _showHardware(): Promise<void> {
|
||||
let hardware;
|
||||
try {
|
||||
hardware = await fetchHassioHardwareInfo(this.hass);
|
||||
} catch (err: any) {
|
||||
await showAlertDialog(this, {
|
||||
const content = await fetchHassioHardwareInfo(this.hass);
|
||||
showHassioMarkdownDialog(this, {
|
||||
title: this.supervisor.localize("system.host.hardware"),
|
||||
content: `<pre>${dump(content, { indent: 2 })}</pre>`,
|
||||
});
|
||||
} catch (err) {
|
||||
showAlertDialog(this, {
|
||||
title: this.supervisor.localize(
|
||||
"system.host.failed_to_get_hardware_list"
|
||||
),
|
||||
text: extractApiErrorMessage(err),
|
||||
});
|
||||
return;
|
||||
}
|
||||
showHassioHardwareDialog(this, { supervisor: this.supervisor, hardware });
|
||||
}
|
||||
|
||||
private async _hostReboot(ev: CustomEvent): Promise<void> {
|
||||
@@ -288,7 +263,7 @@ class HassioHostInfo extends LitElement {
|
||||
|
||||
try {
|
||||
await rebootHost(this.hass);
|
||||
} catch (err: any) {
|
||||
} catch (err) {
|
||||
// Ignore connection errors, these are all expected
|
||||
if (this.hass.connection.connected && !ignoreSupervisorError(err)) {
|
||||
showAlertDialog(this, {
|
||||
@@ -318,7 +293,7 @@ class HassioHostInfo extends LitElement {
|
||||
|
||||
try {
|
||||
await shutdownHost(this.hass);
|
||||
} catch (err: any) {
|
||||
} catch (err) {
|
||||
// Ignore connection errors, these are all expected
|
||||
if (this.hass.connection.connected && !ignoreSupervisorError(err)) {
|
||||
showAlertDialog(this, {
|
||||
@@ -359,7 +334,7 @@ class HassioHostInfo extends LitElement {
|
||||
try {
|
||||
await updateOS(this.hass);
|
||||
fireEvent(this, "supervisor-collection-refresh", { collection: "os" });
|
||||
} catch (err: any) {
|
||||
} catch (err) {
|
||||
if (this.hass.connection.connected) {
|
||||
showAlertDialog(this, {
|
||||
title: this.supervisor.localize(
|
||||
@@ -397,7 +372,7 @@ class HassioHostInfo extends LitElement {
|
||||
fireEvent(this, "supervisor-collection-refresh", {
|
||||
collection: "host",
|
||||
});
|
||||
} catch (err: any) {
|
||||
} catch (err) {
|
||||
showAlertDialog(this, {
|
||||
title: this.supervisor.localize("system.host.failed_to_set_hostname"),
|
||||
text: extractApiErrorMessage(err),
|
||||
@@ -412,7 +387,7 @@ class HassioHostInfo extends LitElement {
|
||||
fireEvent(this, "supervisor-collection-refresh", {
|
||||
collection: "host",
|
||||
});
|
||||
} catch (err: any) {
|
||||
} catch (err) {
|
||||
showAlertDialog(this, {
|
||||
title: this.supervisor.localize(
|
||||
"system.host.failed_to_import_from_usb"
|
||||
|
@@ -3,7 +3,6 @@ import { customElement, property, state } from "lit/decorators";
|
||||
import { atLeastVersion } from "../../../src/common/config/version";
|
||||
import { fireEvent } from "../../../src/common/dom/fire_event";
|
||||
import "../../../src/components/buttons/ha-progress-button";
|
||||
import "../../../src/components/ha-alert";
|
||||
import "../../../src/components/ha-card";
|
||||
import "../../../src/components/ha-settings-row";
|
||||
import "../../../src/components/ha-switch";
|
||||
@@ -34,18 +33,16 @@ import { hassioStyle } from "../resources/hassio-style";
|
||||
const UNSUPPORTED_REASON_URL = {
|
||||
apparmor: "/more-info/unsupported/apparmor",
|
||||
container: "/more-info/unsupported/container",
|
||||
content_trust: "/more-info/unsupported/content_trust",
|
||||
dbus: "/more-info/unsupported/dbus",
|
||||
docker_configuration: "/more-info/unsupported/docker_configuration",
|
||||
docker_version: "/more-info/unsupported/docker_version",
|
||||
job_conditions: "/more-info/unsupported/job_conditions",
|
||||
lxc: "/more-info/unsupported/lxc",
|
||||
network_manager: "/more-info/unsupported/network_manager",
|
||||
os_agent: "/more-info/unsupported/os_agent",
|
||||
os: "/more-info/unsupported/os",
|
||||
privileged: "/more-info/unsupported/privileged",
|
||||
source_mods: "/more-info/unsupported/source_mods",
|
||||
systemd: "/more-info/unsupported/systemd",
|
||||
content_trust: "/more-info/unsupported/content_trust",
|
||||
};
|
||||
|
||||
const UNHEALTHY_REASON_URL = {
|
||||
@@ -173,25 +170,31 @@ class HassioSupervisorInfo extends LitElement {
|
||||
></ha-switch>
|
||||
</ha-settings-row>`
|
||||
: ""
|
||||
: html`<ha-alert
|
||||
alert-type="warning"
|
||||
.actionText=${this.supervisor.localize("common.learn_more")}
|
||||
@alert-action-clicked=${this._unsupportedDialog}
|
||||
>
|
||||
: html`<div class="error">
|
||||
${this.supervisor.localize(
|
||||
"system.supervisor.unsupported_title"
|
||||
)}
|
||||
</ha-alert>`}
|
||||
<button
|
||||
class="link"
|
||||
.title=${this.supervisor.localize("common.learn_more")}
|
||||
@click=${this._unsupportedDialog}
|
||||
>
|
||||
Learn more
|
||||
</button>
|
||||
</div>`}
|
||||
${!this.supervisor.supervisor.healthy
|
||||
? html`<ha-alert
|
||||
alert-type="error"
|
||||
.actionText=${this.supervisor.localize("common.learn_more")}
|
||||
@alert-action-clicked=${this._unhealthyDialog}
|
||||
>
|
||||
? html`<div class="error">
|
||||
${this.supervisor.localize(
|
||||
"system.supervisor.unhealthy_title"
|
||||
)}
|
||||
</ha-alert>`
|
||||
<button
|
||||
class="link"
|
||||
.title=${this.supervisor.localize("common.learn_more")}
|
||||
@click=${this._unhealthyDialog}
|
||||
>
|
||||
Learn more
|
||||
</button>
|
||||
</div>`
|
||||
: ""}
|
||||
</div>
|
||||
<div class="metrics-block">
|
||||
@@ -282,7 +285,7 @@ class HassioSupervisorInfo extends LitElement {
|
||||
};
|
||||
await setSupervisorOption(this.hass, data);
|
||||
await this._reloadSupervisor();
|
||||
} catch (err: any) {
|
||||
} catch (err) {
|
||||
showAlertDialog(this, {
|
||||
title: this.supervisor.localize(
|
||||
"system.supervisor.failed_to_set_option"
|
||||
@@ -300,7 +303,7 @@ class HassioSupervisorInfo extends LitElement {
|
||||
|
||||
try {
|
||||
await this._reloadSupervisor();
|
||||
} catch (err: any) {
|
||||
} catch (err) {
|
||||
showAlertDialog(this, {
|
||||
title: this.supervisor.localize("system.supervisor.failed_to_reload"),
|
||||
text: extractApiErrorMessage(err),
|
||||
@@ -343,7 +346,7 @@ class HassioSupervisorInfo extends LitElement {
|
||||
|
||||
try {
|
||||
await restartSupervisor(this.hass);
|
||||
} catch (err: any) {
|
||||
} catch (err) {
|
||||
showAlertDialog(this, {
|
||||
title: this.supervisor.localize(
|
||||
"common.failed_to_restart_name",
|
||||
@@ -388,7 +391,7 @@ class HassioSupervisorInfo extends LitElement {
|
||||
fireEvent(this, "supervisor-collection-refresh", {
|
||||
collection: "supervisor",
|
||||
});
|
||||
} catch (err: any) {
|
||||
} catch (err) {
|
||||
showAlertDialog(this, {
|
||||
title: this.supervisor.localize(
|
||||
"common.failed_to_update_name",
|
||||
@@ -427,10 +430,10 @@ class HassioSupervisorInfo extends LitElement {
|
||||
<li>
|
||||
${UNSUPPORTED_REASON_URL[reason]
|
||||
? html`<a
|
||||
href=${documentationUrl(
|
||||
href="${documentationUrl(
|
||||
this.hass,
|
||||
UNSUPPORTED_REASON_URL[reason]
|
||||
)}
|
||||
)}"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
@@ -458,10 +461,10 @@ class HassioSupervisorInfo extends LitElement {
|
||||
<li>
|
||||
${UNHEALTHY_REASON_URL[reason]
|
||||
? html`<a
|
||||
href=${documentationUrl(
|
||||
href="${documentationUrl(
|
||||
this.hass,
|
||||
UNHEALTHY_REASON_URL[reason]
|
||||
)}
|
||||
)}"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
@@ -483,7 +486,7 @@ class HassioSupervisorInfo extends LitElement {
|
||||
diagnostics: !this.supervisor.supervisor?.diagnostics,
|
||||
};
|
||||
await setSupervisorOption(this.hass, data);
|
||||
} catch (err: any) {
|
||||
} catch (err) {
|
||||
showAlertDialog(this, {
|
||||
title: this.supervisor.localize(
|
||||
"system.supervisor.failed_to_set_option"
|
||||
|
@@ -5,7 +5,6 @@ import "@polymer/paper-listbox/paper-listbox";
|
||||
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import "../../../src/components/buttons/ha-progress-button";
|
||||
import "../../../src/components/ha-alert";
|
||||
import "../../../src/components/ha-card";
|
||||
import { extractApiErrorMessage } from "../../../src/data/hassio/common";
|
||||
import { fetchHassioLogs } from "../../../src/data/hassio/supervisor";
|
||||
@@ -68,9 +67,7 @@ class HassioSupervisorLog extends LitElement {
|
||||
protected render(): TemplateResult | void {
|
||||
return html`
|
||||
<ha-card>
|
||||
${this._error
|
||||
? html`<ha-alert alert-type="error">${this._error}</ha-alert>`
|
||||
: ""}
|
||||
${this._error ? html` <div class="errors">${this._error}</div> ` : ""}
|
||||
${this.hass.userData?.showAdvanced
|
||||
? html`
|
||||
<paper-dropdown-menu
|
||||
@@ -130,7 +127,7 @@ class HassioSupervisorLog extends LitElement {
|
||||
this.hass,
|
||||
this._selectedLogProvider
|
||||
);
|
||||
} catch (err: any) {
|
||||
} catch (err) {
|
||||
this._error = this.supervisor.localize(
|
||||
"system.log.get_logs",
|
||||
"provider",
|
||||
@@ -157,6 +154,10 @@ class HassioSupervisorLog extends LitElement {
|
||||
padding: 0 2%;
|
||||
width: 96%;
|
||||
}
|
||||
.errors {
|
||||
color: var(--error-color);
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
`,
|
||||
];
|
||||
}
|
||||
|
@@ -1,4 +1,5 @@
|
||||
module.exports = {
|
||||
"*.{js,ts}": 'eslint --ignore-pattern "**/build-scripts/**/*.js" --fix',
|
||||
"*.ts": () => "tsc -p tsconfig.json",
|
||||
"*.{js,ts}": "eslint --fix",
|
||||
"!(/translations)*.{js,ts,json,css,md,html}": "prettier --write",
|
||||
};
|
||||
|
@@ -1,3 +0,0 @@
|
||||
[build.environment]
|
||||
YARN_VERSION = "1.22.11"
|
||||
NODE_OPTIONS = "--max_old_space_size=6144"
|
232
package.json
232
package.json
@@ -16,120 +16,121 @@
|
||||
"lint:lit": "lit-analyzer \"**/src/**/*.ts\" --format markdown --outFile result.md",
|
||||
"lint": "yarn run lint:eslint && yarn run lint:prettier && yarn run lint:types",
|
||||
"format": "yarn run format:eslint && yarn run format:prettier",
|
||||
"test": "instant-mocha --webpack-config ./test/webpack.config.js --require ./test/setup.js \"test/**/*.ts\""
|
||||
"mocha": "ts-mocha -p test-mocha/tsconfig.test.json \"test-mocha/**/*.ts\"",
|
||||
"test": "yarn run lint && yarn run mocha"
|
||||
},
|
||||
"author": "Paulus Schoutsen <Paulus@PaulusSchoutsen.nl> (http://paulusschoutsen.nl)",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@braintree/sanitize-url": "^5.0.2",
|
||||
"@codemirror/commands": "^0.19.2",
|
||||
"@codemirror/gutter": "^0.19.1",
|
||||
"@codemirror/highlight": "^0.19.2",
|
||||
"@codemirror/history": "^0.19.0",
|
||||
"@codemirror/legacy-modes": "^0.19.0",
|
||||
"@codemirror/rectangular-selection": "^0.19.0",
|
||||
"@codemirror/search": "^0.19.0",
|
||||
"@codemirror/state": "^0.19.1",
|
||||
"@codemirror/stream-parser": "^0.19.1",
|
||||
"@codemirror/text": "^0.19.2",
|
||||
"@codemirror/view": "^0.19.4",
|
||||
"@formatjs/intl-datetimeformat": "^4.2.4",
|
||||
"@formatjs/intl-getcanonicallocales": "^1.7.3",
|
||||
"@formatjs/intl-locale": "^2.4.38",
|
||||
"@formatjs/intl-numberformat": "^7.2.4",
|
||||
"@formatjs/intl-pluralrules": "^4.1.4",
|
||||
"@formatjs/intl-relativetimeformat": "^9.3.1",
|
||||
"@formatjs/intl-utils": "^3.8.4",
|
||||
"@fullcalendar/common": "5.9.0",
|
||||
"@fullcalendar/core": "5.9.0",
|
||||
"@fullcalendar/daygrid": "5.9.0",
|
||||
"@fullcalendar/interaction": "5.9.0",
|
||||
"@fullcalendar/list": "5.9.0",
|
||||
"@lit-labs/virtualizer": "patch:@lit-labs/virtualizer@0.6.0#./.yarn/patches/@lit-labs/virtualizer/0.7.0.patch",
|
||||
"@material/chips": "13.0.0-canary.65125b3a6.0",
|
||||
"@material/data-table": "13.0.0-canary.65125b3a6.0",
|
||||
"@material/mwc-button": "0.25.1",
|
||||
"@material/mwc-checkbox": "0.25.1",
|
||||
"@material/mwc-circular-progress": "0.25.1",
|
||||
"@material/mwc-dialog": "0.25.1",
|
||||
"@material/mwc-fab": "0.25.1",
|
||||
"@material/mwc-formfield": "0.25.1",
|
||||
"@material/mwc-icon-button": "0.25.1",
|
||||
"@material/mwc-linear-progress": "0.25.1",
|
||||
"@material/mwc-list": "0.25.1",
|
||||
"@material/mwc-menu": "0.25.1",
|
||||
"@material/mwc-radio": "0.25.1",
|
||||
"@material/mwc-ripple": "0.25.1",
|
||||
"@material/mwc-switch": "0.25.1",
|
||||
"@material/mwc-tab": "0.25.1",
|
||||
"@material/mwc-tab-bar": "0.25.1",
|
||||
"@material/top-app-bar": "13.0.0-canary.65125b3a6.0",
|
||||
"@mdi/js": "6.1.95",
|
||||
"@mdi/svg": "6.1.95",
|
||||
"@polymer/app-layout": "^3.1.0",
|
||||
"@braintree/sanitize-url": "^5.0.1",
|
||||
"@codemirror/commands": "^0.18.0",
|
||||
"@codemirror/gutter": "^0.18.0",
|
||||
"@codemirror/highlight": "^0.18.0",
|
||||
"@codemirror/history": "^0.18.0",
|
||||
"@codemirror/legacy-modes": "^0.18.0",
|
||||
"@codemirror/rectangular-selection": "^0.18.0",
|
||||
"@codemirror/search": "^0.18.0",
|
||||
"@codemirror/state": "^0.18.0",
|
||||
"@codemirror/stream-parser": "^0.18.0",
|
||||
"@codemirror/text": "^0.18.0",
|
||||
"@codemirror/view": "^0.18.0",
|
||||
"@formatjs/intl-getcanonicallocales": "^1.5.10",
|
||||
"@formatjs/intl-locale": "^2.4.28",
|
||||
"@formatjs/intl-pluralrules": "^4.0.22",
|
||||
"@fullcalendar/common": "5.1.0",
|
||||
"@fullcalendar/core": "5.1.0",
|
||||
"@fullcalendar/daygrid": "5.1.0",
|
||||
"@fullcalendar/interaction": "5.1.0",
|
||||
"@fullcalendar/list": "5.1.0",
|
||||
"@lit-labs/virtualizer": "^0.6.0",
|
||||
"@material/chips": "=12.0.0-canary.1a8d06483.0",
|
||||
"@material/mwc-button": "canary",
|
||||
"@material/mwc-checkbox": "canary",
|
||||
"@material/mwc-circular-progress": "canary",
|
||||
"@material/mwc-dialog": "canary",
|
||||
"@material/mwc-fab": "canary",
|
||||
"@material/mwc-formfield": "canary",
|
||||
"@material/mwc-icon-button": "canary",
|
||||
"@material/mwc-list": "canary",
|
||||
"@material/mwc-menu": "canary",
|
||||
"@material/mwc-radio": "canary",
|
||||
"@material/mwc-ripple": "canary",
|
||||
"@material/mwc-switch": "canary",
|
||||
"@material/mwc-tab": "canary",
|
||||
"@material/mwc-tab-bar": "canary",
|
||||
"@material/top-app-bar": "=12.0.0-canary.1a8d06483.0",
|
||||
"@mdi/js": "5.9.55",
|
||||
"@mdi/svg": "5.9.55",
|
||||
"@polymer/app-layout": "^3.0.2",
|
||||
"@polymer/app-storage": "^3.0.2",
|
||||
"@polymer/iron-autogrow-textarea": "^3.0.1",
|
||||
"@polymer/iron-flex-layout": "^3.0.1",
|
||||
"@polymer/iron-icon": "^3.0.1",
|
||||
"@polymer/iron-image": "^3.0.1",
|
||||
"@polymer/iron-input": "^3.0.1",
|
||||
"@polymer/iron-overlay-behavior": "^3.0.3",
|
||||
"@polymer/iron-label": "^3.0.1",
|
||||
"@polymer/iron-overlay-behavior": "^3.0.2",
|
||||
"@polymer/iron-resizable-behavior": "^3.0.1",
|
||||
"@polymer/paper-checkbox": "^3.1.0",
|
||||
"@polymer/paper-dialog": "^3.0.1",
|
||||
"@polymer/paper-dialog-behavior": "^3.0.1",
|
||||
"@polymer/paper-dialog-scrollable": "^3.0.1",
|
||||
"@polymer/paper-dropdown-menu": "^3.2.0",
|
||||
"@polymer/paper-input": "^3.2.1",
|
||||
"@polymer/paper-dropdown-menu": "^3.0.1",
|
||||
"@polymer/paper-input": "^3.0.1",
|
||||
"@polymer/paper-item": "^3.0.1",
|
||||
"@polymer/paper-listbox": "^3.0.1",
|
||||
"@polymer/paper-menu-button": "^3.1.0",
|
||||
"@polymer/paper-menu-button": "^3.0.1",
|
||||
"@polymer/paper-progress": "^3.0.1",
|
||||
"@polymer/paper-radio-button": "^3.0.1",
|
||||
"@polymer/paper-radio-group": "^3.0.1",
|
||||
"@polymer/paper-ripple": "^3.0.2",
|
||||
"@polymer/paper-ripple": "^3.0.1",
|
||||
"@polymer/paper-slider": "^3.0.1",
|
||||
"@polymer/paper-styles": "^3.0.1",
|
||||
"@polymer/paper-tabs": "^3.1.0",
|
||||
"@polymer/paper-tabs": "^3.0.1",
|
||||
"@polymer/paper-toast": "^3.0.1",
|
||||
"@polymer/paper-tooltip": "^3.0.1",
|
||||
"@polymer/polymer": "3.4.1",
|
||||
"@thomasloven/round-slider": "0.5.4",
|
||||
"@vaadin/vaadin-combo-box": "^21.0.2",
|
||||
"@vaadin/vaadin-date-picker": "^21.0.2",
|
||||
"@polymer/polymer": "3.1.0",
|
||||
"@thomasloven/round-slider": "0.5.2",
|
||||
"@vaadin/vaadin-combo-box": "^5.0.10",
|
||||
"@vaadin/vaadin-date-picker": "^4.0.7",
|
||||
"@vibrant/color": "^3.2.1-alpha.1",
|
||||
"@vibrant/core": "^3.2.1-alpha.1",
|
||||
"@vibrant/quantizer-mmcq": "^3.2.1-alpha.1",
|
||||
"@vue/web-component-wrapper": "^1.2.0",
|
||||
"@webcomponents/webcomponentsjs": "^2.2.10",
|
||||
"chart.js": "^3.3.2",
|
||||
"@webcomponents/webcomponentsjs": "^2.2.7",
|
||||
"chart.js": "^2.9.4",
|
||||
"chartjs-chart-timeline": "^0.4.0",
|
||||
"comlink": "^4.3.1",
|
||||
"core-js": "^3.15.2",
|
||||
"cropperjs": "^1.5.12",
|
||||
"date-fns": "^2.23.0",
|
||||
"core-js": "^3.6.5",
|
||||
"cropperjs": "^1.5.11",
|
||||
"deep-clone-simple": "^1.1.1",
|
||||
"deep-freeze": "^0.0.1",
|
||||
"fecha": "^4.2.0",
|
||||
"fuse.js": "^6.0.0",
|
||||
"google-timezones-json": "^1.0.2",
|
||||
"hls.js": "^1.0.10",
|
||||
"home-assistant-js-websocket": "^5.11.1",
|
||||
"idb-keyval": "^5.1.3",
|
||||
"intl-messageformat": "^9.9.1",
|
||||
"hls.js": "^1.0.4",
|
||||
"home-assistant-js-websocket": "^5.10.0",
|
||||
"idb-keyval": "^5.0.5",
|
||||
"intl-messageformat": "^9.6.16",
|
||||
"js-yaml": "^4.1.0",
|
||||
"leaflet": "^1.7.1",
|
||||
"leaflet-draw": "^1.0.4",
|
||||
"lit": "^2.0.0",
|
||||
"lit-vaadin-helpers": "^0.2.1",
|
||||
"marked": "^3.0.2",
|
||||
"lit": "^2.0.0-rc.2",
|
||||
"lit-vaadin-helpers": "^0.1.3",
|
||||
"marked": "^2.0.5",
|
||||
"mdn-polyfills": "^5.16.0",
|
||||
"memoize-one": "^5.2.1",
|
||||
"node-vibrant": "3.2.1-alpha.1",
|
||||
"proxy-polyfill": "^0.3.2",
|
||||
"proxy-polyfill": "^0.3.1",
|
||||
"punycode": "^2.1.1",
|
||||
"qrcode": "^1.4.4",
|
||||
"regenerator-runtime": "^0.13.8",
|
||||
"regenerator-runtime": "^0.13.2",
|
||||
"resize-observer-polyfill": "^1.5.1",
|
||||
"roboto-fontface": "^0.10.0",
|
||||
"sortablejs": "^1.14.0",
|
||||
"sortablejs": "^1.10.2",
|
||||
"superstruct": "^0.15.2",
|
||||
"tinykeys": "^1.1.3",
|
||||
"tsparticles": "^1.34.0",
|
||||
"tsparticles": "^1.19.2",
|
||||
"unfetch": "^4.1.0",
|
||||
"vis-data": "^7.1.2",
|
||||
"vis-network": "^8.5.4",
|
||||
@@ -145,18 +146,17 @@
|
||||
"xss": "^1.0.9"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.15.5",
|
||||
"@babel/plugin-external-helpers": "^7.14.5",
|
||||
"@babel/plugin-proposal-class-properties": "^7.14.5",
|
||||
"@babel/plugin-proposal-decorators": "^7.15.4",
|
||||
"@babel/plugin-proposal-nullish-coalescing-operator": "^7.14.5",
|
||||
"@babel/plugin-proposal-object-rest-spread": "^7.15.6",
|
||||
"@babel/plugin-proposal-optional-chaining": "^7.14.5",
|
||||
"@babel/core": "^7.14.3",
|
||||
"@babel/plugin-external-helpers": "^7.12.13",
|
||||
"@babel/plugin-proposal-class-properties": "^7.13.0",
|
||||
"@babel/plugin-proposal-decorators": "^7.13.15",
|
||||
"@babel/plugin-proposal-nullish-coalescing-operator": "^7.13.8",
|
||||
"@babel/plugin-proposal-object-rest-spread": "^7.13.8",
|
||||
"@babel/plugin-proposal-optional-chaining": "^7.13.12",
|
||||
"@babel/plugin-syntax-dynamic-import": "^7.8.3",
|
||||
"@babel/plugin-syntax-import-meta": "^7.10.4",
|
||||
"@babel/plugin-syntax-top-level-await": "^7.14.5",
|
||||
"@babel/preset-env": "^7.15.6",
|
||||
"@babel/preset-typescript": "^7.15.0",
|
||||
"@babel/preset-env": "^7.14.2",
|
||||
"@babel/preset-typescript": "^7.13.0",
|
||||
"@koa/cors": "^3.1.0",
|
||||
"@open-wc/dev-server-hmr": "^0.0.2",
|
||||
"@rollup/plugin-babel": "^5.2.1",
|
||||
@@ -166,31 +166,30 @@
|
||||
"@rollup/plugin-replace": "^2.3.2",
|
||||
"@types/chromecast-caf-receiver": "5.0.12",
|
||||
"@types/chromecast-caf-sender": "^1.0.3",
|
||||
"@types/js-yaml": "^4",
|
||||
"@types/leaflet": "^1",
|
||||
"@types/leaflet-draw": "^1",
|
||||
"@types/marked": "^2",
|
||||
"@types/mocha": "^8",
|
||||
"@types/sortablejs": "^1",
|
||||
"@types/js-yaml": "^4.0.1",
|
||||
"@types/leaflet": "^1.7.0",
|
||||
"@types/leaflet-draw": "^1.0.3",
|
||||
"@types/marked": "^2.0.3",
|
||||
"@types/mocha": "^8.2.2",
|
||||
"@types/sortablejs": "^1.10.6",
|
||||
"@types/webspeechapi": "^0.0.29",
|
||||
"@typescript-eslint/eslint-plugin": "^4.32.0",
|
||||
"@typescript-eslint/parser": "^4.32.0",
|
||||
"@typescript-eslint/eslint-plugin": "^4.22.0",
|
||||
"@typescript-eslint/parser": "^4.22.0",
|
||||
"@web/dev-server": "^0.0.24",
|
||||
"@web/dev-server-rollup": "^0.2.11",
|
||||
"babel-loader": "^8.2.2",
|
||||
"babel-loader": "^8.1.0",
|
||||
"chai": "^4.3.4",
|
||||
"cpx": "^1.5.0",
|
||||
"del": "^4.0.0",
|
||||
"eslint": "^7.32.0",
|
||||
"eslint-config-airbnb-base": "^14.2.1",
|
||||
"eslint-config-airbnb-typescript": "^14.0.0",
|
||||
"eslint": "^7.25.0",
|
||||
"eslint-config-airbnb-typescript": "^12.3.1",
|
||||
"eslint-config-prettier": "^8.3.0",
|
||||
"eslint-import-resolver-webpack": "^0.13.1",
|
||||
"eslint-import-resolver-webpack": "^0.13.0",
|
||||
"eslint-plugin-disable": "^2.0.1",
|
||||
"eslint-plugin-import": "^2.24.2",
|
||||
"eslint-plugin-lit": "^1.5.1",
|
||||
"eslint-plugin-prettier": "^4.0.0",
|
||||
"eslint-plugin-unused-imports": "^1.1.5",
|
||||
"eslint-plugin-wc": "^1.3.2",
|
||||
"eslint-plugin-import": "^2.22.1",
|
||||
"eslint-plugin-lit": "^1.3.0",
|
||||
"eslint-plugin-prettier": "^3.4.0",
|
||||
"eslint-plugin-wc": "^1.3.0",
|
||||
"fancy-log": "^1.3.3",
|
||||
"fs-extra": "^7.0.1",
|
||||
"gulp": "^4.0.2",
|
||||
@@ -201,8 +200,7 @@
|
||||
"gulp-zopfli-green": "^3.0.1",
|
||||
"html-minifier": "^4.0.0",
|
||||
"husky": "^1.3.1",
|
||||
"instant-mocha": "^1.3.1",
|
||||
"lint-staged": "^11.1.2",
|
||||
"lint-staged": "^10.5.4",
|
||||
"lit-analyzer": "^1.2.1",
|
||||
"lodash.template": "^4.5.0",
|
||||
"magic-string": "^0.25.7",
|
||||
@@ -211,7 +209,8 @@
|
||||
"mocha": "^8.4.0",
|
||||
"object-hash": "^2.0.3",
|
||||
"open": "^7.0.4",
|
||||
"prettier": "^2.4.1",
|
||||
"prettier": "^2.0.4",
|
||||
"raw-loader": "^2.0.0",
|
||||
"require-dir": "^1.2.0",
|
||||
"rollup": "^2.8.2",
|
||||
"rollup-plugin-string": "^3.0.0",
|
||||
@@ -221,26 +220,25 @@
|
||||
"sinon": "^11.0.0",
|
||||
"source-map-url": "^0.4.0",
|
||||
"systemjs": "^6.3.2",
|
||||
"terser-webpack-plugin": "^5.2.4",
|
||||
"terser-webpack-plugin": "^5.1.2",
|
||||
"ts-lit-plugin": "^1.2.1",
|
||||
"typescript": "^4.4.3",
|
||||
"ts-mocha": "^8.0.0",
|
||||
"typescript": "^4.2.4",
|
||||
"vinyl-buffer": "^1.0.1",
|
||||
"vinyl-source-stream": "^2.0.0",
|
||||
"webpack": "^5.55.1",
|
||||
"webpack-cli": "^4.8.0",
|
||||
"webpack-dev-server": "^4.3.0",
|
||||
"webpack-manifest-plugin": "^4.0.2",
|
||||
"webpackbar": "^5.0.0-3",
|
||||
"webpack": "^5.24.1",
|
||||
"webpack-cli": "^4.5.0",
|
||||
"webpack-dev-server": "^3.11.2",
|
||||
"webpack-manifest-plugin": "^3.0.0",
|
||||
"workbox-build": "^6.1.5"
|
||||
},
|
||||
"_comment": "Polymer 3.2 contained a bug, fixed in https://github.com/Polymer/polymer/pull/5569, add as patch",
|
||||
"_comment": "Polymer fixed to 3.1 because 3.2 throws on logbook page",
|
||||
"_comment_2": "Fix in https://github.com/Polymer/polymer/pull/5569",
|
||||
"resolutions": {
|
||||
"@polymer/polymer": "patch:@polymer/polymer@3.4.1#./.yarn/patches/@polymer/polymer/pr-5569.patch",
|
||||
"@webcomponents/webcomponentsjs": "^2.2.10",
|
||||
"lit": "^2.0.0",
|
||||
"lit-html": "2.0.0",
|
||||
"lit-element": "3.0.0",
|
||||
"@lit/reactive-element": "1.0.0"
|
||||
"@polymer/polymer": "3.1.0",
|
||||
"lit-html": "2.0.0-rc.3",
|
||||
"lit-element": "3.0.0-rc.2"
|
||||
},
|
||||
"main": "src/home-assistant.js",
|
||||
"husky": {
|
||||
|
22
script/core
22
script/core
@@ -9,6 +9,12 @@ if [ -z "${DEVCONTAINER}" ]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ ! -z "${CODESPACES}" ]; then
|
||||
WORKSPACE="/root/workspace/frontend"
|
||||
else
|
||||
WORKSPACE="/workspaces/frontend"
|
||||
fi
|
||||
|
||||
if [ -z $(which hass) ]; then
|
||||
echo "Installing Home Asstant core from dev."
|
||||
python3 -m pip install --upgrade \
|
||||
@@ -16,9 +22,9 @@ if [ -z $(which hass) ]; then
|
||||
git+git://github.com/home-assistant/home-assistant.git@dev
|
||||
fi
|
||||
|
||||
if [ ! -d "/workspaces/frontend/config" ]; then
|
||||
if [ ! -d "${WORKSPACE}/config" ]; then
|
||||
echo "Creating default configuration."
|
||||
mkdir -p "/workspaces/frontend/config";
|
||||
mkdir -p "${WORKSPACE}/config";
|
||||
hass --script ensure_config -c config
|
||||
echo "demo:
|
||||
|
||||
@@ -26,24 +32,24 @@ logger:
|
||||
default: info
|
||||
logs:
|
||||
homeassistant.components.frontend: debug
|
||||
" >> /workspaces/frontend/config/configuration.yaml
|
||||
" >> "${WORKSPACE}/config/configuration.yaml"
|
||||
|
||||
if [ ! -z "${HASSIO}" ]; then
|
||||
echo "
|
||||
# frontend:
|
||||
# development_repo: /workspaces/frontend
|
||||
# development_repo: ${WORKSPACE}
|
||||
|
||||
hassio:
|
||||
development_repo: /workspaces/frontend" >> /workspaces/frontend/config/configuration.yaml
|
||||
development_repo: ${WORKSPACE}" >> "${WORKSPACE}/config/configuration.yaml"
|
||||
else
|
||||
echo "
|
||||
frontend:
|
||||
development_repo: /workspaces/frontend
|
||||
development_repo: ${WORKSPACE}
|
||||
|
||||
# hassio:
|
||||
# development_repo: /workspaces/frontend" >> /workspaces/frontend/config/configuration.yaml
|
||||
# development_repo: ${WORKSPACE}" >> "${WORKSPACE}/config/configuration.yaml"
|
||||
fi
|
||||
|
||||
fi
|
||||
|
||||
hass -c /workspaces/frontend/config
|
||||
hass -c "${WORKSPACE}/config"
|
||||
|
4
setup.py
4
setup.py
@@ -2,9 +2,9 @@ from setuptools import setup, find_packages
|
||||
|
||||
setup(
|
||||
name="home-assistant-frontend",
|
||||
version="20210930.0",
|
||||
version="20210531.0",
|
||||
description="The Home Assistant frontend",
|
||||
url="https://github.com/home-assistant/frontend",
|
||||
url="https://github.com/home-assistant/home-assistant-polymer",
|
||||
author="The Home Assistant Authors",
|
||||
author_email="hello@home-assistant.io",
|
||||
license="Apache-2.0",
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user