Compare commits

..

9 Commits

Author SHA1 Message Date
Bram Kragten
895333aa05 20240705.0 (#21306) 2024-07-05 13:40:27 +02:00
Bram Kragten
58ba9f628a 20240703.0 (#21264) 2024-07-03 14:27:49 +02:00
Bram Kragten
28ced4bfd3 20240702.0 (#21255) 2024-07-02 21:37:23 +02:00
Paul Bottein
fd6a192db1 20240628.0 (#21223) 2024-06-28 22:04:49 +02:00
Bram Kragten
d72e8c35d8 20240627.0 (#21192) 2024-06-27 20:02:18 +02:00
Paul Bottein
5bc3ad4c63 20240626.2 (#21181) 2024-06-26 23:19:57 +02:00
Paul Bottein
530745d20d Revert "20240626.1" (#21180)
Revert "20240626.1 (#21179)"

This reverts commit a16cae0671.
2024-06-26 23:18:27 +02:00
Paul Bottein
a16cae0671 20240626.1 (#21179)
* Fix undefined value in search (#21175)

* Fix hass object in nested hui-card (#21178)

* Bumped version to 20240626.1
2024-06-26 23:09:42 +02:00
Bram Kragten
8d0c4e4a52 20240626.0 (#21171) 2024-06-26 12:49:50 +02:00
360 changed files with 5896 additions and 12188 deletions

View File

@@ -1,25 +1,28 @@
[modern] [modern]
# Modern builds target recent browsers supporting the latest features to minimize transpilation, polyfills, etc. # Support for dynamic import is the main litmus test for serving modern builds.
# It is served to browsers meeting the following requirements: # Although officially a ES2020 feature, browsers implemented it early, so this
# - released in the last year + current alpha/beta versions # enables all of ES2017 and some features in ES2018.
# - Firefox extended support release (ESR) supports es6-module-dynamic-import
# - with global utilization at or above 0.5%
# - must support dynamic import of ES modules # Exclude Safari 11-12 because of a bug in tagged template literals
# - exclude browsers no longer being maintained # https://bugs.webkit.org/show_bug.cgi?id=190756
# - exclude KaiOS, QQ, and UC browsers due to lack of sufficient feature support data # Note: Dropping version 11 also enables several more ES2018 features
unreleased versions not Safari < 13
last 1 year not iOS < 13
Firefox ESR
>= 0.5% and supports es6-module-dynamic-import # Exclude KaiOS, QQ, and UC browsers due to lack of sufficient feature support data
not dead # Babel ignores these automatically, but we need here for Webpack to output ESM with dynamic imports
not KaiOS > 0 not KaiOS > 0
not QQAndroid > 0 not QQAndroid > 0
not UCAndroid > 0 not UCAndroid > 0
# Exclude unsupported browsers
not dead
[legacy] [legacy]
# Legacy builds are served when modern requirements are not met and support browsers: # Legacy builds are served when modern requirements are not met and support browsers:
# - released in the last 7 years + current alpha/beta versionss # - released in the last 7 years + current alpha/beta versionss
# - with global utilization at or above 0.05% # - with global utilization above 0.05%
# The lattermost query ensures that support for popular old browsers is not dropped too early # The lattermost query ensures that support for popular old browsers is not dropped too early
# (e.g. IE 11, Android 4.4, or Samsung 4). # (e.g. IE 11, Android 4.4, or Samsung 4).
# #
@@ -33,10 +36,4 @@ not UCAndroid > 0
# As of May 2023, only web sockets must be added to the query. # As of May 2023, only web sockets must be added to the query.
unreleased versions unreleased versions
last 7 years last 7 years
>= 0.05% and supports websockets > 0.05% and supports websockets
[legacy-sw]
# Same as legacy plus supports service workers
unreleased versions
last 7 years
>= 0.05% and supports websockets and supports serviceworkers

View File

@@ -26,7 +26,7 @@ jobs:
ref: dev ref: dev
- name: Setup Node - name: Setup Node
uses: actions/setup-node@v4.0.3 uses: actions/setup-node@v4.0.2
with: with:
node-version-file: ".nvmrc" node-version-file: ".nvmrc"
cache: yarn cache: yarn
@@ -62,7 +62,7 @@ jobs:
ref: master ref: master
- name: Setup Node - name: Setup Node
uses: actions/setup-node@v4.0.3 uses: actions/setup-node@v4.0.2
with: with:
node-version-file: ".nvmrc" node-version-file: ".nvmrc"
cache: yarn cache: yarn

View File

@@ -26,7 +26,7 @@ jobs:
- name: Check out files from GitHub - name: Check out files from GitHub
uses: actions/checkout@v4.1.7 uses: actions/checkout@v4.1.7
- name: Setup Node - name: Setup Node
uses: actions/setup-node@v4.0.3 uses: actions/setup-node@v4.0.2
with: with:
node-version-file: ".nvmrc" node-version-file: ".nvmrc"
cache: yarn cache: yarn
@@ -60,7 +60,7 @@ jobs:
- name: Check out files from GitHub - name: Check out files from GitHub
uses: actions/checkout@v4.1.7 uses: actions/checkout@v4.1.7
- name: Setup Node - name: Setup Node
uses: actions/setup-node@v4.0.3 uses: actions/setup-node@v4.0.2
with: with:
node-version-file: ".nvmrc" node-version-file: ".nvmrc"
cache: yarn cache: yarn
@@ -78,7 +78,7 @@ jobs:
- name: Check out files from GitHub - name: Check out files from GitHub
uses: actions/checkout@v4.1.7 uses: actions/checkout@v4.1.7
- name: Setup Node - name: Setup Node
uses: actions/setup-node@v4.0.3 uses: actions/setup-node@v4.0.2
with: with:
node-version-file: ".nvmrc" node-version-file: ".nvmrc"
cache: yarn cache: yarn
@@ -89,7 +89,7 @@ jobs:
env: env:
IS_TEST: "true" IS_TEST: "true"
- name: Upload bundle stats - name: Upload bundle stats
uses: actions/upload-artifact@v4.3.6 uses: actions/upload-artifact@v4.3.3
with: with:
name: frontend-bundle-stats name: frontend-bundle-stats
path: build/stats/*.json path: build/stats/*.json
@@ -102,7 +102,7 @@ jobs:
- name: Check out files from GitHub - name: Check out files from GitHub
uses: actions/checkout@v4.1.7 uses: actions/checkout@v4.1.7
- name: Setup Node - name: Setup Node
uses: actions/setup-node@v4.0.3 uses: actions/setup-node@v4.0.2
with: with:
node-version-file: ".nvmrc" node-version-file: ".nvmrc"
cache: yarn cache: yarn
@@ -113,7 +113,7 @@ jobs:
env: env:
IS_TEST: "true" IS_TEST: "true"
- name: Upload bundle stats - name: Upload bundle stats
uses: actions/upload-artifact@v4.3.6 uses: actions/upload-artifact@v4.3.3
with: with:
name: supervisor-bundle-stats name: supervisor-bundle-stats
path: build/stats/*.json path: build/stats/*.json

View File

@@ -27,7 +27,7 @@ jobs:
ref: dev ref: dev
- name: Setup Node - name: Setup Node
uses: actions/setup-node@v4.0.3 uses: actions/setup-node@v4.0.2
with: with:
node-version-file: ".nvmrc" node-version-file: ".nvmrc"
cache: yarn cache: yarn
@@ -63,7 +63,7 @@ jobs:
ref: master ref: master
- name: Setup Node - name: Setup Node
uses: actions/setup-node@v4.0.3 uses: actions/setup-node@v4.0.2
with: with:
node-version-file: ".nvmrc" node-version-file: ".nvmrc"
cache: yarn cache: yarn

View File

@@ -19,7 +19,7 @@ jobs:
uses: actions/checkout@v4.1.7 uses: actions/checkout@v4.1.7
- name: Setup Node - name: Setup Node
uses: actions/setup-node@v4.0.3 uses: actions/setup-node@v4.0.2
with: with:
node-version-file: ".nvmrc" node-version-file: ".nvmrc"
cache: yarn cache: yarn

View File

@@ -24,7 +24,7 @@ jobs:
uses: actions/checkout@v4.1.7 uses: actions/checkout@v4.1.7
- name: Setup Node - name: Setup Node
uses: actions/setup-node@v4.0.3 uses: actions/setup-node@v4.0.2
with: with:
node-version-file: ".nvmrc" node-version-file: ".nvmrc"
cache: yarn cache: yarn

View File

@@ -28,7 +28,7 @@ jobs:
python-version: ${{ env.PYTHON_VERSION }} python-version: ${{ env.PYTHON_VERSION }}
- name: Setup Node - name: Setup Node
uses: actions/setup-node@v4.0.3 uses: actions/setup-node@v4.0.2
with: with:
node-version-file: ".nvmrc" node-version-file: ".nvmrc"
cache: yarn cache: yarn
@@ -57,14 +57,14 @@ jobs:
run: tar -czvf translations.tar.gz translations run: tar -czvf translations.tar.gz translations
- name: Upload build artifacts - name: Upload build artifacts
uses: actions/upload-artifact@v4.3.6 uses: actions/upload-artifact@v4.3.3
with: with:
name: wheels name: wheels
path: dist/home_assistant_frontend*.whl path: dist/home_assistant_frontend*.whl
if-no-files-found: error if-no-files-found: error
- name: Upload translations - name: Upload translations
uses: actions/upload-artifact@v4.3.6 uses: actions/upload-artifact@v4.3.3
with: with:
name: translations name: translations
path: translations.tar.gz path: translations.tar.gz

View File

@@ -17,7 +17,7 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Send bundle stats and build information to RelativeCI - name: Send bundle stats and build information to RelativeCI
uses: relative-ci/agent-action@v2.1.12 uses: relative-ci/agent-action@v2.1.11
with: with:
key: ${{ secrets[format('RELATIVE_CI_KEY_{0}_{1}', matrix.bundle, matrix.build)] }} key: ${{ secrets[format('RELATIVE_CI_KEY_{0}_{1}', matrix.bundle, matrix.build)] }}
token: ${{ github.token }} token: ${{ github.token }}

View File

@@ -34,7 +34,7 @@ jobs:
python-version: ${{ env.PYTHON_VERSION }} python-version: ${{ env.PYTHON_VERSION }}
- name: Setup Node - name: Setup Node
uses: actions/setup-node@v4.0.3 uses: actions/setup-node@v4.0.2
with: with:
node-version-file: ".nvmrc" node-version-file: ".nvmrc"
cache: yarn cache: yarn
@@ -55,7 +55,7 @@ jobs:
script/release script/release
- name: Upload release assets - name: Upload release assets
uses: softprops/action-gh-release@v2.0.8 uses: softprops/action-gh-release@v2.0.6
with: with:
files: | files: |
dist/*.whl dist/*.whl
@@ -74,9 +74,9 @@ jobs:
echo "home-assistant-frontend==$version" > ./requirements.txt echo "home-assistant-frontend==$version" > ./requirements.txt
- name: Build wheels - name: Build wheels
uses: home-assistant/wheels@2024.07.1 uses: home-assistant/wheels@2024.01.0
with: with:
abi: cp312 abi: cp311
tag: musllinux_1_2 tag: musllinux_1_2
arch: amd64 arch: amd64
wheels-key: ${{ secrets.WHEELS_KEY }} wheels-key: ${{ secrets.WHEELS_KEY }}

View File

@@ -1 +1,4 @@
#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"
yarn run lint-staged --relative --shell "/bin/bash" yarn run lint-staged --relative --shell "/bin/bash"

View File

@@ -1,55 +0,0 @@
diff --git a/build/inject-manifest.js b/build/inject-manifest.js
index 60e3d2bb51c11a19fbbedbad65e101082ec41c36..fed6026630f43f86e25446383982cf6fb694313b 100644
--- a/build/inject-manifest.js
+++ b/build/inject-manifest.js
@@ -104,7 +104,7 @@ async function injectManifest(config) {
replaceString: manifestString,
searchString: options.injectionPoint,
});
- filesToWrite[options.swDest] = source;
+ filesToWrite[options.swDest] = source.replace(url, encodeURI(upath_1.default.basename(destPath)));
filesToWrite[destPath] = map;
}
else {
diff --git a/build/lib/translate-url-to-sourcemap-paths.js b/build/lib/translate-url-to-sourcemap-paths.js
index 3220c5474eeac6e8a56ca9b2ac2bd9be48529e43..5f003879a904d4840529a42dd056d288fd213771 100644
--- a/build/lib/translate-url-to-sourcemap-paths.js
+++ b/build/lib/translate-url-to-sourcemap-paths.js
@@ -22,7 +22,7 @@ function translateURLToSourcemapPaths(url, swSrc, swDest) {
const possibleSrcPath = upath_1.default.resolve(upath_1.default.dirname(swSrc), url);
if (fs_extra_1.default.existsSync(possibleSrcPath)) {
srcPath = possibleSrcPath;
- destPath = upath_1.default.resolve(upath_1.default.dirname(swDest), url);
+ destPath = `${swDest}.map`;
}
else {
warning = `${errors_1.errors['cant-find-sourcemap']} ${possibleSrcPath}`;
diff --git a/src/inject-manifest.ts b/src/inject-manifest.ts
index 8795ddcaa77aea7b0356417e4bc4b19e2b3f860c..fcdc68342d9ac53936c9ed40a9ccfc2f5070cad3 100644
--- a/src/inject-manifest.ts
+++ b/src/inject-manifest.ts
@@ -129,7 +129,10 @@ export async function injectManifest(
searchString: options.injectionPoint!,
});
- filesToWrite[options.swDest] = source;
+ filesToWrite[options.swDest] = source.replace(
+ url!,
+ encodeURI(upath.basename(destPath)),
+ );
filesToWrite[destPath] = map;
} else {
// If there's no sourcemap associated with swSrc, a simple string
diff --git a/src/lib/translate-url-to-sourcemap-paths.ts b/src/lib/translate-url-to-sourcemap-paths.ts
index 072eac40d4ef5d095a01cb7f7e392a9e034853bd..f0bbe69e88ef3a415de18a7e9cb264daea273d71 100644
--- a/src/lib/translate-url-to-sourcemap-paths.ts
+++ b/src/lib/translate-url-to-sourcemap-paths.ts
@@ -28,7 +28,7 @@ export function translateURLToSourcemapPaths(
const possibleSrcPath = upath.resolve(upath.dirname(swSrc), url);
if (fse.existsSync(possibleSrcPath)) {
srcPath = possibleSrcPath;
- destPath = upath.resolve(upath.dirname(swDest), url);
+ destPath = `${swDest}.map`;
} else {
warning = `${errors['cant-find-sourcemap']} ${possibleSrcPath}`;
}

894
.yarn/releases/yarn-4.3.1.cjs vendored Executable file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -6,4 +6,4 @@ enableGlobalCache: false
nodeLinker: node-modules nodeLinker: node-modules
yarnPath: .yarn/releases/yarn-4.4.0.cjs yarnPath: .yarn/releases/yarn-4.3.1.cjs

View File

@@ -47,7 +47,7 @@ module.exports.emptyPackages = ({ latestBuild, isHassioBuild }) =>
module.exports.definedVars = ({ isProdBuild, latestBuild, defineOverlay }) => ({ module.exports.definedVars = ({ isProdBuild, latestBuild, defineOverlay }) => ({
__DEV__: !isProdBuild, __DEV__: !isProdBuild,
__BUILD__: JSON.stringify(latestBuild ? "modern" : "legacy"), __BUILD__: JSON.stringify(latestBuild ? "latest" : "es5"),
__VERSION__: JSON.stringify(env.version()), __VERSION__: JSON.stringify(env.version()),
__DEMO__: false, __DEMO__: false,
__SUPERVISOR__: false, __SUPERVISOR__: false,
@@ -79,12 +79,7 @@ module.exports.terserOptions = ({ latestBuild, isTestBuild }) => ({
sourceMap: !isTestBuild, sourceMap: !isTestBuild,
}); });
module.exports.babelOptions = ({ module.exports.babelOptions = ({ latestBuild, isProdBuild, isTestBuild }) => ({
latestBuild,
isProdBuild,
isTestBuild,
sw,
}) => ({
babelrc: false, babelrc: false,
compact: false, compact: false,
assumptions: { assumptions: {
@@ -92,7 +87,7 @@ module.exports.babelOptions = ({
setPublicClassFields: true, setPublicClassFields: true,
setSpreadProperties: true, setSpreadProperties: true,
}, },
browserslistEnv: latestBuild ? "modern" : `legacy${sw ? "-sw" : ""}`, browserslistEnv: latestBuild ? "modern" : "legacy",
presets: [ presets: [
[ [
"@babel/preset-env", "@babel/preset-env",
@@ -140,14 +135,8 @@ module.exports.babelOptions = ({
"@babel/plugin-transform-runtime", "@babel/plugin-transform-runtime",
{ version: dependencies["@babel/runtime"] }, { version: dependencies["@babel/runtime"] },
], ],
// Transpile decorators (still in TC39 process) // Support some proposals still in TC39 process
// Modern browsers support class fields and private methods, but transform is required with the older decorator version dictated by Lit ["@babel/plugin-proposal-decorators", { decoratorsBeforeExport: true }],
[
"@babel/plugin-proposal-decorators",
{ version: "2018-09", decoratorsBeforeExport: true },
],
"@babel/plugin-transform-class-properties",
"@babel/plugin-transform-private-methods",
].filter(Boolean), ].filter(Boolean),
exclude: [ exclude: [
// \\ for Windows, / for Mac OS and Linux // \\ for Windows, / for Mac OS and Linux
@@ -226,13 +215,7 @@ module.exports.config = {
return { return {
name: "frontend" + nameSuffix(latestBuild), name: "frontend" + nameSuffix(latestBuild),
entry: { entry: {
"service-worker": service_worker: "./src/entrypoints/service_worker.ts",
!env.useRollup() && !latestBuild
? {
import: "./src/entrypoints/service-worker.ts",
layer: "sw",
}
: "./src/entrypoints/service-worker.ts",
app: "./src/entrypoints/app.ts", app: "./src/entrypoints/app.ts",
authorize: "./src/entrypoints/authorize.ts", authorize: "./src/entrypoints/authorize.ts",
onboarding: "./src/entrypoints/onboarding.ts", onboarding: "./src/entrypoints/onboarding.ts",

View File

@@ -1,54 +1,19 @@
// Tasks to compress // Tasks to compress
import { constants } from "node:zlib";
import gulp from "gulp"; import gulp from "gulp";
import brotli from "gulp-brotli";
import zopfli from "gulp-zopfli-green"; import zopfli from "gulp-zopfli-green";
import paths from "../paths.cjs"; import paths from "../paths.cjs";
const filesGlob = "*.{js,json,css,svg,xml}";
const brotliOptions = {
skipLarger: true,
params: {
[constants.BROTLI_PARAM_QUALITY]: constants.BROTLI_MAX_QUALITY,
},
};
const zopfliOptions = { threshold: 150 }; const zopfliOptions = { threshold: 150 };
const compressDistBrotli = (rootDir, modernDir) => const compressDist = (rootDir) =>
gulp gulp
.src([`${modernDir}/**/${filesGlob}`, `${rootDir}/sw-modern.js`], { .src([
base: rootDir, `${rootDir}/**/*.{js,json,css,svg,xml}`,
}) `${rootDir}/{authorize,onboarding}.html`,
.pipe(brotli(brotliOptions)) ])
.pipe(gulp.dest(rootDir));
const compressDistZopfli = (rootDir, modernDir) =>
gulp
.src(
[
`${rootDir}/**/${filesGlob}`,
`!${modernDir}/**/${filesGlob}`,
`!${rootDir}/{sw-modern,service_worker}.js`,
`${rootDir}/{authorize,onboarding}.html`,
],
{ base: rootDir }
)
.pipe(zopfli(zopfliOptions)) .pipe(zopfli(zopfliOptions))
.pipe(gulp.dest(rootDir)); .pipe(gulp.dest(rootDir));
const compressAppBrotli = () => gulp.task("compress-app", () => compressDist(paths.app_output_root));
compressDistBrotli(paths.app_output_root, paths.app_output_latest); gulp.task("compress-hassio", () => compressDist(paths.hassio_output_root));
const compressHassioBrotli = () =>
compressDistBrotli(paths.hassio_output_root, paths.hassio_output_latest);
const compressAppZopfli = () =>
compressDistZopfli(paths.app_output_root, paths.app_output_latest);
const compressHassioZopfli = () =>
compressDistZopfli(paths.hassio_output_root, paths.hassio_output_latest);
gulp.task("compress-app", gulp.parallel(compressAppBrotli, compressAppZopfli));
gulp.task(
"compress-hassio",
gulp.parallel(compressHassioBrotli, compressHassioZopfli)
);

View File

@@ -1,76 +1,28 @@
// Tasks to generate entry HTML // Tasks to generate entry HTML
import {
applyVersionsToRegexes,
compileRegex,
getPreUserAgentRegexes,
} from "browserslist-useragent-regexp";
import fs from "fs-extra"; import fs from "fs-extra";
import gulp from "gulp"; import gulp from "gulp";
import { minify } from "html-minifier-terser"; import { minify } from "html-minifier-terser";
import template from "lodash.template"; import template from "lodash.template";
import { dirname, extname, resolve } from "node:path"; import path from "path";
import { htmlMinifierOptions, terserOptions } from "../bundle.cjs"; import { htmlMinifierOptions, terserOptions } from "../bundle.cjs";
import env from "../env.cjs"; import env from "../env.cjs";
import paths from "../paths.cjs"; import paths from "../paths.cjs";
// macOS companion app has no way to obtain the Safari version used by WKWebView,
// and it is not in the default user agent string. So we add an additional regex
// to serve modern based on a minimum macOS version. We take the minimum Safari
// major version from browserslist and manually map that to a supported macOS
// version. Note this assumes the user has kept Safari updated.
const HA_MACOS_REGEX =
/Home Assistant\/[\d.]+ \(.+; macOS (\d+)\.(\d+)(?:\.(\d+))?\)/;
const SAFARI_TO_MACOS = {
15: [10, 15, 0],
16: [11, 0, 0],
17: [12, 0, 0],
18: [13, 0, 0],
};
const getCommonTemplateVars = () => {
const browserRegexes = getPreUserAgentRegexes({
env: "modern",
allowHigherVersions: true,
mobileToDesktop: true,
throwOnMissing: true,
});
const minSafariVersion = browserRegexes.find(
(regex) => regex.family === "safari"
)?.matchedVersions[0][0];
const minMacOSVersion = SAFARI_TO_MACOS[minSafariVersion];
if (!minMacOSVersion) {
throw Error(
`Could not find minimum MacOS version for Safari ${minSafariVersion}.`
);
}
const haMacOSRegex = applyVersionsToRegexes(
[
{
family: "ha_macos",
regex: HA_MACOS_REGEX,
matchedVersions: [minMacOSVersion],
requestVersions: [minMacOSVersion],
},
],
{ ignorePatch: true, allowHigherVersions: true }
);
return {
useRollup: env.useRollup(),
useWDS: env.useWDS(),
modernRegex: compileRegex(browserRegexes.concat(haMacOSRegex)).toString(),
};
};
const renderTemplate = (templateFile, data = {}) => { const renderTemplate = (templateFile, data = {}) => {
const compiled = template( const compiled = template(
fs.readFileSync(templateFile, { encoding: "utf-8" }) fs.readFileSync(templateFile, { encoding: "utf-8" })
); );
return compiled({ return compiled({
...data, ...data,
useRollup: env.useRollup(),
useWDS: env.useWDS(),
// Resolve any child/nested templates relative to the parent and pass the same data // Resolve any child/nested templates relative to the parent and pass the same data
renderTemplate: (childTemplate) => renderTemplate: (childTemplate) =>
renderTemplate(resolve(dirname(templateFile), childTemplate), data), renderTemplate(
path.resolve(path.dirname(templateFile), childTemplate),
data
),
}); });
}; };
@@ -104,12 +56,10 @@ const genPagesDevTask =
publicRoot = "" publicRoot = ""
) => ) =>
async () => { async () => {
const commonVars = getCommonTemplateVars();
for (const [page, entries] of Object.entries(pageEntries)) { for (const [page, entries] of Object.entries(pageEntries)) {
const content = renderTemplate( const content = renderTemplate(
resolve(inputRoot, inputSub, `${page}.template`), path.resolve(inputRoot, inputSub, `${page}.template`),
{ {
...commonVars,
latestEntryJS: entries.map((entry) => latestEntryJS: entries.map((entry) =>
useWDS useWDS
? `http://localhost:8000/src/entrypoints/${entry}.ts` ? `http://localhost:8000/src/entrypoints/${entry}.ts`
@@ -124,7 +74,7 @@ const genPagesDevTask =
es5CustomPanelJS: `${publicRoot}/frontend_es5/custom-panel.js`, es5CustomPanelJS: `${publicRoot}/frontend_es5/custom-panel.js`,
} }
); );
fs.outputFileSync(resolve(outputRoot, page), content); fs.outputFileSync(path.resolve(outputRoot, page), content);
} }
}; };
@@ -141,18 +91,16 @@ const genPagesProdTask =
) => ) =>
async () => { async () => {
const latestManifest = fs.readJsonSync( const latestManifest = fs.readJsonSync(
resolve(outputLatest, "manifest.json") path.resolve(outputLatest, "manifest.json")
); );
const es5Manifest = outputES5 const es5Manifest = outputES5
? fs.readJsonSync(resolve(outputES5, "manifest.json")) ? fs.readJsonSync(path.resolve(outputES5, "manifest.json"))
: {}; : {};
const commonVars = getCommonTemplateVars();
const minifiedHTML = []; const minifiedHTML = [];
for (const [page, entries] of Object.entries(pageEntries)) { for (const [page, entries] of Object.entries(pageEntries)) {
const content = renderTemplate( const content = renderTemplate(
resolve(inputRoot, inputSub, `${page}.template`), path.resolve(inputRoot, inputSub, `${page}.template`),
{ {
...commonVars,
latestEntryJS: entries.map((entry) => latestManifest[`${entry}.js`]), latestEntryJS: entries.map((entry) => latestManifest[`${entry}.js`]),
es5EntryJS: entries.map((entry) => es5Manifest[`${entry}.js`]), es5EntryJS: entries.map((entry) => es5Manifest[`${entry}.js`]),
latestCustomPanelJS: latestManifest["custom-panel.js"], latestCustomPanelJS: latestManifest["custom-panel.js"],
@@ -160,8 +108,8 @@ const genPagesProdTask =
} }
); );
minifiedHTML.push( minifiedHTML.push(
minifyHtml(content, extname(page)).then((minified) => minifyHtml(content, path.extname(page)).then((minified) =>
fs.outputFileSync(resolve(outputRoot, page), minified) fs.outputFileSync(path.resolve(outputRoot, page), minified)
) )
); );
} }

View File

@@ -1,19 +1,20 @@
// Generate service workers // Generate service worker.
// Based on manifest, create a file with the content as service_worker.js
import { deleteAsync } from "del"; import fs from "fs-extra";
import gulp from "gulp"; import gulp from "gulp";
import { mkdir, readFile, symlink, writeFile } from "node:fs/promises"; import path from "path";
import { basename, join, relative } from "node:path"; import sourceMapUrl from "source-map-url";
import { injectManifest } from "workbox-build"; import workboxBuild from "workbox-build";
import paths from "../paths.cjs"; import paths from "../paths.cjs";
const SW_MAP = { const swDest = path.resolve(paths.app_output_root, "service_worker.js");
[paths.app_output_latest]: "modern",
[paths.app_output_es5]: "legacy",
};
const SW_DEV = const writeSW = (content) => fs.outputFileSync(swDest, content.trim() + "\n");
`
gulp.task("gen-service-worker-app-dev", (done) => {
writeSW(
`
console.debug('Service worker disabled in development'); console.debug('Service worker disabled in development');
self.addEventListener('install', (event) => { self.addEventListener('install', (event) => {
@@ -21,67 +22,72 @@ self.addEventListener('install', (event) => {
// removing any prod service worker the dev might have running // removing any prod service worker the dev might have running
self.skipWaiting(); self.skipWaiting();
}); });
`.trim() + "\n"; `
gulp.task("gen-service-worker-app-dev", async () => {
await mkdir(paths.app_output_root, { recursive: true });
await Promise.all(
Object.values(SW_MAP).map((build) =>
writeFile(join(paths.app_output_root, `sw-${build}.js`), SW_DEV, {
encoding: "utf-8",
})
)
); );
done();
}); });
gulp.task("gen-service-worker-app-prod", () => gulp.task("gen-service-worker-app-prod", async () => {
Promise.all( // Read bundled source file
Object.entries(SW_MAP).map(async ([outPath, build]) => { const bundleManifestLatest = fs.readJsonSync(
const manifest = JSON.parse( path.resolve(paths.app_output_latest, "manifest.json")
await readFile(join(outPath, "manifest.json"), "utf-8") );
); let serviceWorkerContent = fs.readFileSync(
const swSrc = join(paths.app_output_root, manifest["service-worker.js"]); paths.app_output_root + bundleManifestLatest["service_worker.js"],
const swDest = join(paths.app_output_root, `sw-${build}.js`); "utf-8"
const buildDir = relative(paths.app_output_root, outPath); );
const { warnings } = await injectManifest({
swSrc, // Delete old file from frontend_latest so manifest won't pick it up
swDest, fs.removeSync(
injectionPoint: "__WB_MANIFEST__", paths.app_output_root + bundleManifestLatest["service_worker.js"]
// Files that mach this pattern will be considered unique and skip revision check );
// ignore JS files + translation files fs.removeSync(
dontCacheBustURLsMatching: new RegExp( paths.app_output_root + bundleManifestLatest["service_worker.js.map"]
`(?:${buildDir}/.+|static/translations/.+)` );
),
globDirectory: paths.app_output_root, // Remove ES5
globPatterns: [ const bundleManifestES5 = fs.readJsonSync(
`${buildDir}/*.js`, path.resolve(paths.app_output_es5, "manifest.json")
// Cache all English translations because we catch them as fallback );
// Using pattern to match hash instead of * to avoid caching en-GB fs.removeSync(paths.app_output_root + bundleManifestES5["service_worker.js"]);
// 'v' added as valid hash letter because in dev we hash with 'dev' fs.removeSync(
"static/translations/**/en-+([a-fv0-9]).json", paths.app_output_root + bundleManifestES5["service_worker.js.map"]
// Icon shown on splash screen );
"static/icons/favicon-192x192.png",
"static/icons/favicon.ico", const workboxManifest = await workboxBuild.getManifest({
// Common fonts // Files that mach this pattern will be considered unique and skip revision check
"static/fonts/roboto/Roboto-Light.woff2", // ignore JS files + translation files
"static/fonts/roboto/Roboto-Medium.woff2", dontCacheBustURLsMatching: /(frontend_latest\/.+|static\/translations\/.+)/,
"static/fonts/roboto/Roboto-Regular.woff2",
"static/fonts/roboto/Roboto-Bold.woff2", globDirectory: paths.app_output_root,
], globPatterns: [
globIgnores: [`${buildDir}/service-worker*`], "frontend_latest/*.js",
}); // Cache all English translations because we catch them as fallback
if (warnings.length > 0) { // Using pattern to match hash instead of * to avoid caching en-GB
console.warn( // 'v' added as valid hash letter because in dev we hash with 'dev'
`Problems while injecting ${build} service worker:\n`, "static/translations/**/en-+([a-fv0-9]).json",
warnings.join("\n") // Icon shown on splash screen
); "static/icons/favicon-192x192.png",
} "static/icons/favicon.ico",
await deleteAsync(`${swSrc}?(.map)`); // Common fonts
// Needed to install new SW from a cached HTML "static/fonts/roboto/Roboto-Light.woff2",
if (build === "modern") { "static/fonts/roboto/Roboto-Medium.woff2",
const swOld = join(paths.app_output_root, "service_worker.js"); "static/fonts/roboto/Roboto-Regular.woff2",
await symlink(basename(swDest), swOld); "static/fonts/roboto/Roboto-Bold.woff2",
} ],
}) });
)
); for (const warning of workboxManifest.warnings) {
console.warn(warning);
}
// remove source map and add WB manifest
serviceWorkerContent = sourceMapUrl.removeFrom(serviceWorkerContent);
serviceWorkerContent = serviceWorkerContent.replace(
"WB_MANIFEST",
JSON.stringify(workboxManifest.manifestEntries)
);
// Write new file to root
fs.writeFileSync(swDest, serviceWorkerContent);
});

View File

@@ -63,19 +63,14 @@ const createWebpackConfig = ({
rules: [ rules: [
{ {
test: /\.m?js$|\.ts$/, test: /\.m?js$|\.ts$/,
use: (info) => ({ use: {
loader: "babel-loader", loader: "babel-loader",
options: { options: {
...bundle.babelOptions({ ...bundle.babelOptions({ latestBuild, isProdBuild, isTestBuild }),
latestBuild,
isProdBuild,
isTestBuild,
sw: info.issuerLayer === "sw",
}),
cacheDirectory: !isProdBuild, cacheDirectory: !isProdBuild,
cacheCompression: false, cacheCompression: false,
}, },
}), },
resolve: { resolve: {
fullySpecified: false, fullySpecified: false,
}, },
@@ -240,7 +235,6 @@ const createWebpackConfig = ({
), ),
}, },
experiments: { experiments: {
layers: true,
outputModule: true, outputModule: true,
}, },
}; };

View File

@@ -1,5 +0,0 @@
"use strict";
self.addEventListener("fetch", (event) => {
event.respondWith(fetch(event.request));
});

View File

@@ -36,7 +36,13 @@
</head> </head>
<body> <body>
<%= renderTemplate("../../../src/html/_js_base.html.template") %> <%= renderTemplate("../../../src/html/_js_base.html.template") %>
<%= renderTemplate("../../../src/html/_script_loader.html.template") %> <script>
<% for (const entry of latestEntryJS) { %>
import("<%= entry %>");
<% } %>
window.latestJS = true;
</script>
<%= renderTemplate("../../../src/html/_script_load_es5.html.template") %>
<hc-layout subtitle="FAQ"> <hc-layout subtitle="FAQ">
<style> <style>
a { a {

View File

@@ -13,9 +13,15 @@
<%= renderTemplate("_social_meta.html.template") %> <%= renderTemplate("_social_meta.html.template") %>
</head> </head>
<body> <body>
<hc-connect></hc-connect>
<%= renderTemplate("../../../src/html/_js_base.html.template") %> <%= renderTemplate("../../../src/html/_js_base.html.template") %>
<%= renderTemplate("../../../src/html/_script_loader.html.template") %> <hc-connect></hc-connect>
<script>
<% for (const entry of latestEntryJS) { %>
import("<%= entry %>");
<% } %>
window.latestJS = true;
</script>
<%= renderTemplate("../../../src/html/_script_load_es5.html.template") %>
<script> <script>
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){ (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o), (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),

View File

@@ -16,8 +16,14 @@
</style> </style>
</head> </head>
<body> <body>
<cast-media-player></cast-media-player>
<%= renderTemplate("../../../src/html/_js_base.html.template") %> <%= renderTemplate("../../../src/html/_js_base.html.template") %>
<%= renderTemplate("../../../src/html/_script_loader.html.template") %> <cast-media-player></cast-media-player>
<script>
<% for (const entry of latestEntryJS) { %>
import("<%= entry %>");
<% } %>
window.latestJS = true;
</script>
<%= renderTemplate("../../../src/html/_script_load_es5.html.template") %>
</body> </body>
</html> </html>

View File

@@ -1,5 +0,0 @@
"use strict";
self.addEventListener("fetch", (event) => {
event.respondWith(fetch(event.request));
});

View File

@@ -82,8 +82,6 @@ export class HaDemo extends HomeAssistantAppEl {
has_entity_name: false, has_entity_name: false,
unique_id: "co2_intensity", unique_id: "co2_intensity",
options: null, options: null,
created_at: 0,
modified_at: 0,
}, },
{ {
config_entry_id: "co2signal", config_entry_id: "co2signal",
@@ -102,8 +100,6 @@ export class HaDemo extends HomeAssistantAppEl {
has_entity_name: false, has_entity_name: false,
unique_id: "grid_fossil_fuel_percentage", unique_id: "grid_fossil_fuel_percentage",
options: null, options: null,
created_at: 0,
modified_at: 0,
}, },
]); ]);

View File

@@ -63,47 +63,35 @@
align-items: center; align-items: center;
} }
#ha-launch-screen svg { #ha-launch-screen svg {
width: 112px; width: 170px;
flex-shrink: 0; flex-shrink: 0;
} }
#ha-launch-screen .ha-launch-screen-spacer-top { #ha-launch-screen .ha-launch-screen-spacer {
flex: 1; flex: 1;
margin-top: calc( 2 * max(env(safe-area-inset-bottom), 48px) + 46px );
padding-top: 48px;
}
#ha-launch-screen .ha-launch-screen-spacer-bottom {
flex: 1;
padding-top: 48px;
}
.ohf-logo {
margin: max(env(safe-area-inset-bottom), 48px) 0;
display: flex;
flex-direction: column;
align-items: center;
opacity: .66;
}
@media (prefers-color-scheme: dark) {
.ohf-logo {
filter: invert(1);
}
} }
</style> </style>
</head> </head>
<body> <body>
<div id="ha-launch-screen"> <div id="ha-launch-screen">
<div class="ha-launch-screen-spacer-top"></div> <div class="ha-launch-screen-spacer"></div>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 240 240"> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 240 240">
<path fill="#18BCF2" d="M240 224.762a15 15 0 0 1-15 15H15a15 15 0 0 1-15-15v-90c0-8.25 4.77-19.769 10.61-25.609l98.78-98.7805c5.83-5.83 15.38-5.83 21.21 0l98.79 98.7895c5.83 5.83 10.61 17.36 10.61 25.61v90-.01Z"/> <path fill="#18BCF2" d="M240 224.762a15 15 0 0 1-15 15H15a15 15 0 0 1-15-15v-90c0-8.25 4.77-19.769 10.61-25.609l98.78-98.7805c5.83-5.83 15.38-5.83 21.21 0l98.79 98.7895c5.83 5.83 10.61 17.36 10.61 25.61v90-.01Z"/>
<path fill="#F2F4F9" d="m107.27 239.762-40.63-40.63c-2.09.72-4.32 1.13-6.64 1.13-11.3 0-20.5-9.2-20.5-20.5s9.2-20.5 20.5-20.5 20.5 9.2 20.5 20.5c0 2.33-.41 4.56-1.13 6.65l31.63 31.63v-115.88c-6.8-3.3395-11.5-10.3195-11.5-18.3895 0-11.3 9.2-20.5 20.5-20.5s20.5 9.2 20.5 20.5c0 8.07-4.7 15.05-11.5 18.3895v81.27l31.46-31.46c-.62-1.96-.96-4.04-.96-6.2 0-11.3 9.2-20.5 20.5-20.5s20.5 9.2 20.5 20.5-9.2 20.5-20.5 20.5c-2.5 0-4.88-.47-7.09-1.29L129 208.892v30.88z"/> <path fill="#F2F4F9" d="m107.27 239.762-40.63-40.63c-2.09.72-4.32 1.13-6.64 1.13-11.3 0-20.5-9.2-20.5-20.5s9.2-20.5 20.5-20.5 20.5 9.2 20.5 20.5c0 2.33-.41 4.56-1.13 6.65l31.63 31.63v-115.88c-6.8-3.3395-11.5-10.3195-11.5-18.3895 0-11.3 9.2-20.5 20.5-20.5s20.5 9.2 20.5 20.5c0 8.07-4.7 15.05-11.5 18.3895v81.27l31.46-31.46c-.62-1.96-.96-4.04-.96-6.2 0-11.3 9.2-20.5 20.5-20.5s20.5 9.2 20.5 20.5-9.2 20.5-20.5 20.5c-2.5 0-4.88-.47-7.09-1.29L129 208.892v30.88z"/>
</svg> </svg>
<div id="ha-launch-screen-info-box" class="ha-launch-screen-spacer-bottom"></div> <div id="ha-launch-screen-info-box" class="ha-launch-screen-spacer"></div>
<div class="ohf-logo">
<img src="/static/images/ohf-badge.svg" alt="Home Assistant is a project by the Open Home Foundation" height="46">
</div>
</div> </div>
<ha-demo></ha-demo> <ha-demo></ha-demo>
<%= renderTemplate("../../../src/html/_js_base.html.template") %> <%= renderTemplate("../../../src/html/_js_base.html.template") %>
<%= renderTemplate("../../../src/html/_preload_roboto.html.template") %> <%= renderTemplate("../../../src/html/_preload_roboto.html.template") %>
<%= renderTemplate("../../../src/html/_script_loader.html.template") %> <script>
// Safari 12 and below does not have a compliant ES2015 implementation of template literals, so we ship ES5
if (!isS11_12) {
<% for (const entry of latestEntryJS) { %>
import("<%= entry %>");
<% } %>
window.latestJS = true;
}
</script>
<%= renderTemplate("../../../src/html/_script_load_es5.html.template") %>
</body> </body>
</html> </html>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 110 KiB

View File

@@ -532,6 +532,15 @@ export default {
last_changed: "2018-07-19T10:44:46.200946+00:00", last_changed: "2018-07-19T10:44:46.200946+00:00",
last_updated: "2018-07-19T10:44:46.200946+00:00", last_updated: "2018-07-19T10:44:46.200946+00:00",
}, },
"mailbox.demomailbox": {
entity_id: "mailbox.demomailbox",
state: "10",
attributes: {
friendly_name: "DemoMailbox",
},
last_changed: "2018-07-19T10:45:16.555210+00:00",
last_updated: "2018-07-19T10:45:16.555210+00:00",
},
"input_select.living_room_preset": { "input_select.living_room_preset": {
entity_id: "input_select.living_room_preset", entity_id: "input_select.living_room_preset",
state: "Visitors", state: "Visitors",

View File

@@ -15,7 +15,6 @@ import { getEntity } from "../../../../src/fake_data/entity";
import { provideHass } from "../../../../src/fake_data/provide_hass"; import { provideHass } from "../../../../src/fake_data/provide_hass";
import { HomeAssistant } from "../../../../src/types"; import { HomeAssistant } from "../../../../src/types";
import "../../components/demo-black-white-row"; import "../../components/demo-black-white-row";
import { DeviceRegistryEntry } from "../../../../src/data/device_registry";
const ENTITIES = [ const ENTITIES = [
getEntity("alarm_control_panel", "alarm", "disarmed", { getEntity("alarm_control_panel", "alarm", "disarmed", {
@@ -42,7 +41,7 @@ const ENTITIES = [
}), }),
]; ];
const DEVICES: DeviceRegistryEntry[] = [ const DEVICES = [
{ {
area_id: "bedroom", area_id: "bedroom",
configuration_url: null, configuration_url: null,
@@ -54,7 +53,6 @@ const DEVICES: DeviceRegistryEntry[] = [
identifiers: [["demo", "volume1"] as [string, string]], identifiers: [["demo", "volume1"] as [string, string]],
manufacturer: null, manufacturer: null,
model: null, model: null,
model_id: null,
name_by_user: null, name_by_user: null,
name: "Dishwasher", name: "Dishwasher",
sw_version: null, sw_version: null,
@@ -62,8 +60,6 @@ const DEVICES: DeviceRegistryEntry[] = [
via_device_id: null, via_device_id: null,
serial_number: null, serial_number: null,
labels: [], labels: [],
created_at: 0,
modified_at: 0,
}, },
{ {
area_id: "backyard", area_id: "backyard",
@@ -76,7 +72,6 @@ const DEVICES: DeviceRegistryEntry[] = [
identifiers: [["demo", "pwm1"] as [string, string]], identifiers: [["demo", "pwm1"] as [string, string]],
manufacturer: null, manufacturer: null,
model: null, model: null,
model_id: null,
name_by_user: null, name_by_user: null,
name: "Lamp", name: "Lamp",
sw_version: null, sw_version: null,
@@ -84,8 +79,6 @@ const DEVICES: DeviceRegistryEntry[] = [
via_device_id: null, via_device_id: null,
serial_number: null, serial_number: null,
labels: [], labels: [],
created_at: 0,
modified_at: 0,
}, },
{ {
area_id: null, area_id: null,
@@ -98,7 +91,6 @@ const DEVICES: DeviceRegistryEntry[] = [
identifiers: [["demo", "pwm1"] as [string, string]], identifiers: [["demo", "pwm1"] as [string, string]],
manufacturer: null, manufacturer: null,
model: null, model: null,
model_id: null,
name_by_user: "User name", name_by_user: "User name",
name: "Technical name", name: "Technical name",
sw_version: null, sw_version: null,
@@ -106,8 +98,6 @@ const DEVICES: DeviceRegistryEntry[] = [
via_device_id: null, via_device_id: null,
serial_number: null, serial_number: null,
labels: [], labels: [],
created_at: 0,
modified_at: 0,
}, },
]; ];
@@ -120,8 +110,6 @@ const AREAS: AreaRegistryEntry[] = [
picture: null, picture: null,
aliases: [], aliases: [],
labels: [], labels: [],
created_at: 0,
modified_at: 0,
}, },
{ {
area_id: "bedroom", area_id: "bedroom",
@@ -131,8 +119,6 @@ const AREAS: AreaRegistryEntry[] = [
picture: null, picture: null,
aliases: [], aliases: [],
labels: [], labels: [],
created_at: 0,
modified_at: 0,
}, },
{ {
area_id: "livingroom", area_id: "livingroom",
@@ -142,8 +128,6 @@ const AREAS: AreaRegistryEntry[] = [
picture: null, picture: null,
aliases: [], aliases: [],
labels: [], labels: [],
created_at: 0,
modified_at: 0,
}, },
]; ];

View File

@@ -21,7 +21,6 @@ import { FloorRegistryEntry } from "../../../../src/data/floor_registry";
import { LabelRegistryEntry } from "../../../../src/data/label_registry"; import { LabelRegistryEntry } from "../../../../src/data/label_registry";
import { mockFloorRegistry } from "../../../../demo/src/stubs/floor_registry"; import { mockFloorRegistry } from "../../../../demo/src/stubs/floor_registry";
import { mockLabelRegistry } from "../../../../demo/src/stubs/label_registry"; import { mockLabelRegistry } from "../../../../demo/src/stubs/label_registry";
import { DeviceRegistryEntry } from "../../../../src/data/device_registry";
const ENTITIES = [ const ENTITIES = [
getEntity("alarm_control_panel", "alarm", "disarmed", { getEntity("alarm_control_panel", "alarm", "disarmed", {
@@ -42,7 +41,7 @@ const ENTITIES = [
}), }),
]; ];
const DEVICES: DeviceRegistryEntry[] = [ const DEVICES = [
{ {
area_id: "bedroom", area_id: "bedroom",
configuration_url: null, configuration_url: null,
@@ -54,7 +53,6 @@ const DEVICES: DeviceRegistryEntry[] = [
identifiers: [["demo", "volume1"] as [string, string]], identifiers: [["demo", "volume1"] as [string, string]],
manufacturer: null, manufacturer: null,
model: null, model: null,
model_id: null,
name_by_user: null, name_by_user: null,
name: "Dishwasher", name: "Dishwasher",
sw_version: null, sw_version: null,
@@ -62,8 +60,6 @@ const DEVICES: DeviceRegistryEntry[] = [
via_device_id: null, via_device_id: null,
serial_number: null, serial_number: null,
labels: [], labels: [],
created_at: 0,
modified_at: 0,
}, },
{ {
area_id: "backyard", area_id: "backyard",
@@ -76,7 +72,6 @@ const DEVICES: DeviceRegistryEntry[] = [
identifiers: [["demo", "pwm1"] as [string, string]], identifiers: [["demo", "pwm1"] as [string, string]],
manufacturer: null, manufacturer: null,
model: null, model: null,
model_id: null,
name_by_user: null, name_by_user: null,
name: "Lamp", name: "Lamp",
sw_version: null, sw_version: null,
@@ -84,8 +79,6 @@ const DEVICES: DeviceRegistryEntry[] = [
via_device_id: null, via_device_id: null,
serial_number: null, serial_number: null,
labels: [], labels: [],
created_at: 0,
modified_at: 0,
}, },
{ {
area_id: null, area_id: null,
@@ -98,7 +91,6 @@ const DEVICES: DeviceRegistryEntry[] = [
identifiers: [["demo", "pwm1"] as [string, string]], identifiers: [["demo", "pwm1"] as [string, string]],
manufacturer: null, manufacturer: null,
model: null, model: null,
model_id: null,
name_by_user: "User name", name_by_user: "User name",
name: "Technical name", name: "Technical name",
sw_version: null, sw_version: null,
@@ -106,8 +98,6 @@ const DEVICES: DeviceRegistryEntry[] = [
via_device_id: null, via_device_id: null,
serial_number: null, serial_number: null,
labels: [], labels: [],
created_at: 0,
modified_at: 0,
}, },
]; ];
@@ -120,8 +110,6 @@ const AREAS: AreaRegistryEntry[] = [
picture: null, picture: null,
aliases: [], aliases: [],
labels: [], labels: [],
created_at: 0,
modified_at: 0,
}, },
{ {
area_id: "bedroom", area_id: "bedroom",
@@ -131,8 +119,6 @@ const AREAS: AreaRegistryEntry[] = [
picture: null, picture: null,
aliases: [], aliases: [],
labels: [], labels: [],
created_at: 0,
modified_at: 0,
}, },
{ {
area_id: "livingroom", area_id: "livingroom",
@@ -142,8 +128,6 @@ const AREAS: AreaRegistryEntry[] = [
picture: null, picture: null,
aliases: [], aliases: [],
labels: [], labels: [],
created_at: 0,
modified_at: 0,
}, },
]; ];
@@ -154,8 +138,6 @@ const FLOORS: FloorRegistryEntry[] = [
level: 0, level: 0,
icon: null, icon: null,
aliases: [], aliases: [],
created_at: 0,
modified_at: 0,
}, },
{ {
floor_id: "first", floor_id: "first",
@@ -163,8 +145,6 @@ const FLOORS: FloorRegistryEntry[] = [
level: 1, level: 1,
icon: "mdi:numeric-1", icon: "mdi:numeric-1",
aliases: [], aliases: [],
created_at: 0,
modified_at: 0,
}, },
{ {
floor_id: "second", floor_id: "second",
@@ -172,8 +152,6 @@ const FLOORS: FloorRegistryEntry[] = [
level: 2, level: 2,
icon: "mdi:numeric-2", icon: "mdi:numeric-2",
aliases: [], aliases: [],
created_at: 0,
modified_at: 0,
}, },
]; ];
@@ -184,8 +162,6 @@ const LABELS: LabelRegistryEntry[] = [
icon: null, icon: null,
color: "yellow", color: "yellow",
description: null, description: null,
created_at: 0,
modified_at: 0,
}, },
{ {
label_id: "entertainment", label_id: "entertainment",
@@ -193,8 +169,6 @@ const LABELS: LabelRegistryEntry[] = [
icon: "mdi:popcorn", icon: "mdi:popcorn",
color: "blue", color: "blue",
description: null, description: null,
created_at: 0,
modified_at: 0,
}, },
]; ];

View File

@@ -287,11 +287,11 @@ const CONFIGS = [
config: ` config: `
- type: entities - type: entities
entities: entities:
- type: perform-action - type: call-service
icon: mdi:power icon: mdi:power
name: Bed light name: Bed light
action_name: Toggle light action_name: Toggle light
action: light.toggle service: light.toggle
data: data:
entity_id: light.bed_light entity_id: light.bed_light
- type: section - type: section

View File

@@ -1,3 +0,0 @@
---
title: Picture Card
---

View File

@@ -1,61 +0,0 @@
import { html, LitElement, PropertyValues, TemplateResult } from "lit";
import { customElement, query } from "lit/decorators";
import { getEntity } from "../../../../src/fake_data/entity";
import { provideHass } from "../../../../src/fake_data/provide_hass";
import "../../components/demo-cards";
import { mockIcons } from "../../../../demo/src/stubs/icons";
const ENTITIES = [
getEntity("person", "paulus", "home", {
friendly_name: "Paulus",
entity_picture: "/images/paulus.jpg",
}),
];
const CONFIGS = [
{
heading: "Image URL",
config: `
- type: picture
image: /images/living_room.png
`,
},
{
heading: "Person entity",
config: `
- type: picture
image_entity: person.paulus
`,
},
{
heading: "Error: Image required",
config: `
- type: picture
entity: person.paulus
`,
},
];
@customElement("demo-lovelace-picture-card")
class DemoPicture extends LitElement {
@query("#demos") private _demoRoot!: HTMLElement;
protected render(): TemplateResult {
return html`<demo-cards id="demos" .configs=${CONFIGS}></demo-cards>`;
}
protected firstUpdated(changedProperties: PropertyValues) {
super.firstUpdated(changedProperties);
const hass = provideHass(this._demoRoot);
hass.updateTranslations(null, "en");
hass.updateTranslations("lovelace", "en");
hass.addEntities(ENTITIES);
mockIcons(hass);
}
}
declare global {
interface HTMLElementTagNameMap {
"demo-lovelace-picture-card": DemoPicture;
}
}

View File

@@ -25,15 +25,6 @@ const ENTITIES = [
friendly_name: "Movement Backyard", friendly_name: "Movement Backyard",
device_class: "motion", device_class: "motion",
}), }),
getEntity("person", "paulus", "home", {
friendly_name: "Paulus",
entity_picture: "/images/paulus.jpg",
}),
getEntity("sensor", "battery", 35, {
device_class: "battery",
friendly_name: "Battery",
unit_of_measurement: "%",
}),
]; ];
const CONFIGS = [ const CONFIGS = [
@@ -132,19 +123,6 @@ const CONFIGS = [
left: 35% left: 35%
`, `,
}, },
{
heading: "Person entity",
config: `
- type: picture-elements
image_entity: person.paulus
elements:
- type: state-icon
entity: sensor.battery
style:
top: 8%
left: 8%
`,
},
]; ];
@customElement("demo-lovelace-picture-elements-card") @customElement("demo-lovelace-picture-elements-card")

View File

@@ -12,10 +12,6 @@ const ENTITIES = [
getEntity("light", "bed_light", "off", { getEntity("light", "bed_light", "off", {
friendly_name: "Bed Light", friendly_name: "Bed Light",
}), }),
getEntity("person", "paulus", "home", {
friendly_name: "Paulus",
entity_picture: "/images/paulus.jpg",
}),
]; ];
const CONFIGS = [ const CONFIGS = [
@@ -54,13 +50,6 @@ const CONFIGS = [
entity: camera.demo_camera entity: camera.demo_camera
`, `,
}, },
{
heading: "Person entity",
config: `
- type: picture-entity
entity: person.paulus
`,
},
{ {
heading: "Hidden name", heading: "Hidden name",
config: ` config: `

View File

@@ -20,15 +20,6 @@ const ENTITIES = [
friendly_name: "Basement Floor Wet", friendly_name: "Basement Floor Wet",
device_class: "moisture", device_class: "moisture",
}), }),
getEntity("person", "paulus", "home", {
friendly_name: "Paulus",
entity_picture: "/images/paulus.jpg",
}),
getEntity("sensor", "battery", 35, {
device_class: "battery",
friendly_name: "Battery",
unit_of_measurement: "%",
}),
]; ];
const CONFIGS = [ const CONFIGS = [
@@ -99,15 +90,6 @@ const CONFIGS = [
- light.ceiling_lights - light.ceiling_lights
`, `,
}, },
{
heading: "Person entity",
config: `
- type: picture-glance
image_entity: person.paulus
entities:
- sensor.battery
`,
},
{ {
heading: "Custom icon", heading: "Custom icon",
config: ` config: `

View File

@@ -8,7 +8,6 @@ import { getEntity } from "../../../../src/fake_data/entity";
import { provideHass } from "../../../../src/fake_data/provide_hass"; import { provideHass } from "../../../../src/fake_data/provide_hass";
import "../../components/demo-cards"; import "../../components/demo-cards";
import { mockIcons } from "../../../../demo/src/stubs/icons"; import { mockIcons } from "../../../../demo/src/stubs/icons";
import { ClimateEntityFeature } from "../../../../src/data/climate";
const ENTITIES = [ const ENTITIES = [
getEntity("switch", "tv_outlet", "on", { getEntity("switch", "tv_outlet", "on", {
@@ -61,36 +60,6 @@ const ENTITIES = [
CoverEntityFeature.OPEN_TILT + CoverEntityFeature.OPEN_TILT +
CoverEntityFeature.STOP_TILT, CoverEntityFeature.STOP_TILT,
}), }),
getEntity("input_number", "counter", "1.0", {
friendly_name: "Counter",
initial: 0,
min: 0,
max: 100,
step: 1,
mode: "slider",
}),
getEntity("climate", "dual_thermostat", "heat/cool", {
friendly_name: "Dual thermostat",
hvac_modes: ["off", "cool", "heat_cool", "auto", "dry", "fan_only"],
min_temp: 7,
max_temp: 35,
fan_modes: ["on_low", "on_high", "auto_low", "auto_high", "off"],
preset_modes: ["home", "eco", "away"],
swing_modes: ["auto", "1", "2", "3", "off"],
current_temperature: 23,
target_temp_high: 24,
target_temp_low: 21,
fan_mode: "auto_low",
preset_mode: "home",
swing_mode: "auto",
supported_features:
ClimateEntityFeature.TURN_ON +
ClimateEntityFeature.TURN_OFF +
ClimateEntityFeature.SWING_MODE +
ClimateEntityFeature.PRESET_MODE +
ClimateEntityFeature.FAN_MODE +
ClimateEntityFeature.TARGET_TEMPERATURE_RANGE,
}),
]; ];
const CONFIGS = [ const CONFIGS = [
@@ -224,25 +193,6 @@ const CONFIGS = [
- type: "cover-tilt" - type: "cover-tilt"
`, `,
}, },
{
heading: "Number buttons feature",
config: `
- type: tile
entity: input_number.counter
features:
- type: numeric-input
style: buttons
`,
},
{
heading: "Dual thermostat feature",
config: `
- type: tile
entity: climate.dual_thermostat
features:
- type: target-temperature
`,
},
]; ];
@customElement("demo-lovelace-tile-card") @customElement("demo-lovelace-tile-card")

View File

@@ -140,9 +140,6 @@ const ENTITIES: HassEntity[] = [
createEntity("climate.auto_preheating", "auto", undefined, { createEntity("climate.auto_preheating", "auto", undefined, {
hvac_action: "preheating", hvac_action: "preheating",
}), }),
createEntity("climate.auto_defrosting", "auto", undefined, {
hvac_action: "defrosting",
}),
createEntity("climate.auto_heating", "auto", undefined, { createEntity("climate.auto_heating", "auto", undefined, {
hvac_action: "heating", hvac_action: "heating",
}), }),
@@ -358,11 +355,13 @@ export class DemoEntityState extends LitElement {
}, },
entity_id: { entity_id: {
title: "Entity ID", title: "Entity ID",
width: "30%",
filterable: true, filterable: true,
sortable: true, sortable: true,
}, },
state: { state: {
title: "State", title: "State",
width: "20%",
sortable: true, sortable: true,
template: (entry) => template: (entry) =>
html`${computeStateDisplay( html`${computeStateDisplay(
@@ -377,12 +376,14 @@ export class DemoEntityState extends LitElement {
device_class: { device_class: {
title: "Device class", title: "Device class",
template: (entry) => html`${entry.device_class ?? "-"}`, template: (entry) => html`${entry.device_class ?? "-"}`,
width: "20%",
filterable: true, filterable: true,
sortable: true, sortable: true,
}, },
domain: { domain: {
title: "Domain", title: "Domain",
template: (entry) => html`${computeDomain(entry.entity_id)}`, template: (entry) => html`${computeDomain(entry.entity_id)}`,
width: "20%",
filterable: true, filterable: true,
sortable: true, sortable: true,
}, },

View File

@@ -203,8 +203,6 @@ const createEntityRegistryEntries = (
options: null, options: null,
labels: [], labels: [],
categories: {}, categories: {},
created_at: 0,
modified_at: 0,
}, },
]; ];
@@ -217,7 +215,6 @@ const createDeviceRegistryEntries = (
connections: [], connections: [],
manufacturer: "ESPHome", manufacturer: "ESPHome",
model: "Mock Device", model: "Mock Device",
model_id: "ABC-001",
name: "Tag Reader", name: "Tag Reader",
sw_version: null, sw_version: null,
hw_version: "1.0.0", hw_version: "1.0.0",
@@ -230,8 +227,6 @@ const createDeviceRegistryEntries = (
disabled_by: null, disabled_by: null,
configuration_url: null, configuration_url: null,
labels: [], labels: [],
created_at: 0,
modified_at: 0,
}, },
]; ];

View File

@@ -127,13 +127,14 @@ export class HassioBackups extends LitElement {
main: true, main: true,
sortable: true, sortable: true,
filterable: true, filterable: true,
flex: 2, grows: true,
template: (backup) => template: (backup) =>
html`${backup.name || backup.slug} html`${backup.name || backup.slug}
<div class="secondary">${backup.secondary}</div>`, <div class="secondary">${backup.secondary}</div>`,
}, },
size: { size: {
title: this.supervisor.localize("backup.size"), title: this.supervisor.localize("backup.size"),
width: "15%",
hidden: narrow, hidden: narrow,
filterable: true, filterable: true,
sortable: true, sortable: true,
@@ -141,6 +142,7 @@ export class HassioBackups extends LitElement {
}, },
location: { location: {
title: this.supervisor.localize("backup.location"), title: this.supervisor.localize("backup.location"),
width: "15%",
hidden: narrow, hidden: narrow,
filterable: true, filterable: true,
sortable: true, sortable: true,
@@ -149,6 +151,7 @@ export class HassioBackups extends LitElement {
}, },
date: { date: {
title: this.supervisor.localize("backup.created"), title: this.supervisor.localize("backup.created"),
width: "15%",
direction: "desc", direction: "desc",
hidden: narrow, hidden: narrow,
filterable: true, filterable: true,

View File

@@ -66,8 +66,7 @@ class HassioRepositoriesDialog extends LitElement {
repo.slug !== "core" && // The core add-ons repository repo.slug !== "core" && // The core add-ons repository
repo.slug !== "local" && // Locally managed add-ons repo.slug !== "local" && // Locally managed add-ons
repo.slug !== "a0d7b954" && // Home Assistant Community Add-ons repo.slug !== "a0d7b954" && // Home Assistant Community Add-ons
repo.slug !== "5c53de3b" && // The ESPHome repository repo.slug !== "5c53de3b" // The ESPHome repository
repo.slug !== "d5369777" // Music Assistant repository
) )
.sort((a, b) => .sort((a, b) =>
caseInsensitiveStringCompare(a.name, b.name, this.hass.locale.language) caseInsensitiveStringCompare(a.name, b.name, this.hass.locale.language)

View File

@@ -4,7 +4,11 @@
el.src = src; el.src = src;
document.body.appendChild(el); document.body.appendChild(el);
} }
if (<%= modernRegex %>.test(navigator.userAgent)) { if (/.*Version\/(?:11|12)(?:\.\d+)*.*Safari\//.test(navigator.userAgent)) {
<% for (const entry of es5EntryJS) { %>
loadES5("<%= entry %>");
<% } %>
} else {
try { try {
<% for (const entry of latestEntryJS) { %> <% for (const entry of latestEntryJS) { %>
new Function("import('<%= entry %>')")(); new Function("import('<%= entry %>')")();
@@ -13,10 +17,6 @@
<% for (const entry of es5EntryJS) { %> <% for (const entry of es5EntryJS) { %>
loadES5("<%= entry %>"); loadES5("<%= entry %>");
<% } %> <% } %>
} else {
<% for (const entry of es5EntryJS) { %>
loadES5("<%= entry %>");
<% } %>
} }
} }
})(); })();

View File

@@ -16,7 +16,7 @@
"lint:lit": "lit-analyzer \"{.,*}/src/**/*.ts\"", "lint:lit": "lit-analyzer \"{.,*}/src/**/*.ts\"",
"lint": "yarn run lint:eslint && yarn run lint:prettier && yarn run lint:types && yarn run lint:lit", "lint": "yarn run lint:eslint && yarn run lint:prettier && yarn run lint:types && yarn run lint:lit",
"format": "yarn run format:eslint && yarn run format:prettier", "format": "yarn run format:eslint && yarn run format:prettier",
"postinstall": "husky", "postinstall": "husky install",
"prepack": "pinst --disable", "prepack": "pinst --disable",
"postpack": "pinst --enable", "postpack": "pinst --enable",
"test": "instant-mocha --webpack-config ./test/webpack.config.js --require ./test/setup.cjs \"test/**/*.ts\"" "test": "instant-mocha --webpack-config ./test/webpack.config.js --require ./test/setup.cjs \"test/**/*.ts\""
@@ -25,15 +25,15 @@
"license": "Apache-2.0", "license": "Apache-2.0",
"type": "module", "type": "module",
"dependencies": { "dependencies": {
"@babel/runtime": "7.25.4", "@babel/runtime": "7.24.7",
"@braintree/sanitize-url": "7.1.0", "@braintree/sanitize-url": "7.0.3",
"@codemirror/autocomplete": "6.18.0", "@codemirror/autocomplete": "6.16.3",
"@codemirror/commands": "6.6.0", "@codemirror/commands": "6.6.0",
"@codemirror/language": "6.10.2", "@codemirror/language": "6.10.2",
"@codemirror/legacy-modes": "6.4.1", "@codemirror/legacy-modes": "6.4.0",
"@codemirror/search": "6.5.6", "@codemirror/search": "6.5.6",
"@codemirror/state": "6.4.1", "@codemirror/state": "6.4.1",
"@codemirror/view": "6.32.0", "@codemirror/view": "6.28.3",
"@egjs/hammerjs": "2.0.17", "@egjs/hammerjs": "2.0.17",
"@formatjs/intl-datetimeformat": "6.12.5", "@formatjs/intl-datetimeformat": "6.12.5",
"@formatjs/intl-displaynames": "6.6.8", "@formatjs/intl-displaynames": "6.6.8",
@@ -43,17 +43,17 @@
"@formatjs/intl-numberformat": "8.10.3", "@formatjs/intl-numberformat": "8.10.3",
"@formatjs/intl-pluralrules": "5.2.14", "@formatjs/intl-pluralrules": "5.2.14",
"@formatjs/intl-relativetimeformat": "11.2.14", "@formatjs/intl-relativetimeformat": "11.2.14",
"@fullcalendar/core": "6.1.15", "@fullcalendar/core": "6.1.11",
"@fullcalendar/daygrid": "6.1.15", "@fullcalendar/daygrid": "6.1.11",
"@fullcalendar/interaction": "6.1.15", "@fullcalendar/interaction": "6.1.11",
"@fullcalendar/list": "6.1.15", "@fullcalendar/list": "6.1.11",
"@fullcalendar/luxon3": "6.1.15", "@fullcalendar/luxon3": "6.1.11",
"@fullcalendar/timegrid": "6.1.15", "@fullcalendar/timegrid": "6.1.11",
"@lezer/highlight": "1.2.1", "@lezer/highlight": "1.2.0",
"@lit-labs/context": "0.4.1", "@lit-labs/context": "0.4.1",
"@lit-labs/motion": "1.0.7", "@lit-labs/motion": "1.0.7",
"@lit-labs/observers": "2.0.2", "@lit-labs/observers": "2.0.2",
"@lit-labs/virtualizer": "2.0.14", "@lit-labs/virtualizer": "2.0.13",
"@lrnwebcomponents/simple-tooltip": "8.0.2", "@lrnwebcomponents/simple-tooltip": "8.0.2",
"@material/chips": "=14.0.0-canary.53b3cad2f.0", "@material/chips": "=14.0.0-canary.53b3cad2f.0",
"@material/data-table": "=14.0.0-canary.53b3cad2f.0", "@material/data-table": "=14.0.0-canary.53b3cad2f.0",
@@ -80,7 +80,7 @@
"@material/mwc-top-app-bar": "0.27.0", "@material/mwc-top-app-bar": "0.27.0",
"@material/mwc-top-app-bar-fixed": "0.27.0", "@material/mwc-top-app-bar-fixed": "0.27.0",
"@material/top-app-bar": "=14.0.0-canary.53b3cad2f.0", "@material/top-app-bar": "=14.0.0-canary.53b3cad2f.0",
"@material/web": "2.1.0", "@material/web": "1.5.1",
"@mdi/js": "7.4.47", "@mdi/js": "7.4.47",
"@mdi/svg": "7.4.47", "@mdi/svg": "7.4.47",
"@polymer/paper-item": "3.0.1", "@polymer/paper-item": "3.0.1",
@@ -88,8 +88,8 @@
"@polymer/paper-tabs": "3.1.0", "@polymer/paper-tabs": "3.1.0",
"@polymer/polymer": "3.5.1", "@polymer/polymer": "3.5.1",
"@thomasloven/round-slider": "0.6.0", "@thomasloven/round-slider": "0.6.0",
"@vaadin/combo-box": "24.4.6", "@vaadin/combo-box": "24.4.0",
"@vaadin/vaadin-themable-mixin": "24.4.6", "@vaadin/vaadin-themable-mixin": "24.4.0",
"@vibrant/color": "3.2.1-alpha.1", "@vibrant/color": "3.2.1-alpha.1",
"@vibrant/core": "3.2.1-alpha.1", "@vibrant/core": "3.2.1-alpha.1",
"@vibrant/quantizer-mmcq": "3.2.1-alpha.1", "@vibrant/quantizer-mmcq": "3.2.1-alpha.1",
@@ -97,10 +97,10 @@
"@webcomponents/scoped-custom-element-registry": "0.0.9", "@webcomponents/scoped-custom-element-registry": "0.0.9",
"@webcomponents/webcomponentsjs": "2.8.0", "@webcomponents/webcomponentsjs": "2.8.0",
"app-datepicker": "5.1.1", "app-datepicker": "5.1.1",
"chart.js": "4.4.4", "chart.js": "4.4.3",
"color-name": "2.0.0", "color-name": "2.0.0",
"comlink": "4.4.1", "comlink": "4.4.1",
"core-js": "3.38.1", "core-js": "3.37.1",
"cropperjs": "1.6.2", "cropperjs": "1.6.2",
"date-fns": "3.6.0", "date-fns": "3.6.0",
"date-fns-tz": "3.1.3", "date-fns-tz": "3.1.3",
@@ -117,20 +117,20 @@
"leaflet": "1.9.4", "leaflet": "1.9.4",
"leaflet-draw": "1.0.4", "leaflet-draw": "1.0.4",
"lit": "2.8.0", "lit": "2.8.0",
"luxon": "3.5.0", "luxon": "3.4.4",
"marked": "14.0.0", "marked": "12.0.2",
"memoize-one": "6.0.0", "memoize-one": "6.0.0",
"node-vibrant": "3.2.1-alpha.1", "node-vibrant": "3.2.1-alpha.1",
"proxy-polyfill": "0.3.2", "proxy-polyfill": "0.3.2",
"punycode": "2.3.1", "punycode": "2.3.1",
"qr-scanner": "1.4.2", "qr-scanner": "1.4.2",
"qrcode": "1.5.4", "qrcode": "1.5.3",
"roboto-fontface": "0.10.0", "roboto-fontface": "0.10.0",
"rrule": "2.8.1", "rrule": "2.8.1",
"sortablejs": "1.15.2", "sortablejs": "1.15.2",
"stacktrace-js": "2.0.2", "stacktrace-js": "2.0.2",
"superstruct": "2.0.2", "superstruct": "1.0.4",
"tinykeys": "3.0.0", "tinykeys": "2.1.0",
"tsparticles-engine": "2.12.0", "tsparticles-engine": "2.12.0",
"tsparticles-preset-links": "2.12.0", "tsparticles-preset-links": "2.12.0",
"ua-parser-js": "1.0.38", "ua-parser-js": "1.0.38",
@@ -149,18 +149,18 @@
"xss": "1.0.15" "xss": "1.0.15"
}, },
"devDependencies": { "devDependencies": {
"@babel/core": "7.25.2", "@babel/core": "7.24.7",
"@babel/helper-define-polyfill-provider": "0.6.2", "@babel/helper-define-polyfill-provider": "0.6.2",
"@babel/plugin-proposal-decorators": "7.24.7", "@babel/plugin-proposal-decorators": "7.24.7",
"@babel/plugin-transform-runtime": "7.25.4", "@babel/plugin-transform-runtime": "7.24.7",
"@babel/preset-env": "7.25.4", "@babel/preset-env": "7.24.7",
"@babel/preset-typescript": "7.24.7", "@babel/preset-typescript": "7.24.7",
"@bundle-stats/plugin-webpack-filter": "4.14.2", "@bundle-stats/plugin-webpack-filter": "4.13.3",
"@koa/cors": "5.0.0", "@koa/cors": "5.0.0",
"@lokalise/node-api": "12.7.0", "@lokalise/node-api": "12.6.0",
"@octokit/auth-oauth-device": "7.1.1", "@octokit/auth-oauth-device": "7.1.1",
"@octokit/plugin-retry": "7.1.1", "@octokit/plugin-retry": "7.1.1",
"@octokit/rest": "21.0.2", "@octokit/rest": "21.0.0",
"@open-wc/dev-server-hmr": "0.1.4", "@open-wc/dev-server-hmr": "0.1.4",
"@rollup/plugin-babel": "6.0.4", "@rollup/plugin-babel": "6.0.4",
"@rollup/plugin-commonjs": "26.0.1", "@rollup/plugin-commonjs": "26.0.1",
@@ -168,7 +168,7 @@
"@rollup/plugin-node-resolve": "15.2.3", "@rollup/plugin-node-resolve": "15.2.3",
"@rollup/plugin-replace": "5.0.7", "@rollup/plugin-replace": "5.0.7",
"@types/babel__plugin-transform-runtime": "7.9.5", "@types/babel__plugin-transform-runtime": "7.9.5",
"@types/chromecast-caf-receiver": "6.0.17", "@types/chromecast-caf-receiver": "6.0.16",
"@types/chromecast-caf-sender": "1.0.10", "@types/chromecast-caf-sender": "1.0.10",
"@types/color-name": "1.1.4", "@types/color-name": "1.1.4",
"@types/glob": "8.1.0", "@types/glob": "8.1.0",
@@ -185,13 +185,12 @@
"@types/tar": "6.1.13", "@types/tar": "6.1.13",
"@types/ua-parser-js": "0.7.39", "@types/ua-parser-js": "0.7.39",
"@types/webspeechapi": "0.0.29", "@types/webspeechapi": "0.0.29",
"@typescript-eslint/eslint-plugin": "7.18.0", "@typescript-eslint/eslint-plugin": "7.15.0",
"@typescript-eslint/parser": "7.18.0", "@typescript-eslint/parser": "7.15.0",
"@web/dev-server": "0.1.38", "@web/dev-server": "0.1.38",
"@web/dev-server-rollup": "0.4.1", "@web/dev-server-rollup": "0.4.1",
"babel-loader": "9.1.3", "babel-loader": "9.1.3",
"babel-plugin-template-html-minifier": "4.1.0", "babel-plugin-template-html-minifier": "4.1.0",
"browserslist-useragent-regexp": "4.1.3",
"chai": "5.1.1", "chai": "5.1.1",
"del": "7.1.0", "del": "7.1.0",
"eslint": "8.57.0", "eslint": "8.57.0",
@@ -201,51 +200,51 @@
"eslint-import-resolver-webpack": "0.13.8", "eslint-import-resolver-webpack": "0.13.8",
"eslint-plugin-import": "2.29.1", "eslint-plugin-import": "2.29.1",
"eslint-plugin-lit": "1.14.0", "eslint-plugin-lit": "1.14.0",
"eslint-plugin-lit-a11y": "4.1.4", "eslint-plugin-lit-a11y": "4.1.3",
"eslint-plugin-unused-imports": "4.1.3", "eslint-plugin-unused-imports": "4.0.0",
"eslint-plugin-wc": "2.1.1", "eslint-plugin-wc": "2.1.0",
"fancy-log": "2.0.0", "fancy-log": "2.0.0",
"fs-extra": "11.2.0", "fs-extra": "11.2.0",
"glob": "11.0.0", "glob": "10.4.2",
"gulp": "5.0.0", "gulp": "5.0.0",
"gulp-brotli": "3.0.0",
"gulp-json-transform": "0.5.0", "gulp-json-transform": "0.5.0",
"gulp-rename": "2.0.0", "gulp-rename": "2.0.0",
"gulp-zopfli-green": "6.0.2", "gulp-zopfli-green": "6.0.1",
"html-minifier-terser": "7.2.0", "html-minifier-terser": "7.2.0",
"husky": "9.1.5", "husky": "9.0.11",
"instant-mocha": "1.5.2", "instant-mocha": "1.5.2",
"jszip": "3.10.1", "jszip": "3.10.1",
"lint-staged": "15.2.9", "lint-staged": "15.2.7",
"lit-analyzer": "2.0.3", "lit-analyzer": "2.0.3",
"lodash.merge": "4.6.2", "lodash.merge": "4.6.2",
"lodash.template": "4.5.0", "lodash.template": "4.5.0",
"magic-string": "0.30.11", "magic-string": "0.30.10",
"map-stream": "0.0.7", "map-stream": "0.0.7",
"mocha": "10.5.0", "mocha": "10.5.0",
"object-hash": "3.0.0", "object-hash": "3.0.0",
"open": "10.1.0", "open": "10.1.0",
"pinst": "3.0.0", "pinst": "3.0.0",
"prettier": "3.3.3", "prettier": "3.3.2",
"rollup": "2.79.1", "rollup": "2.79.1",
"rollup-plugin-string": "3.0.0", "rollup-plugin-string": "3.0.0",
"rollup-plugin-terser": "7.0.2", "rollup-plugin-terser": "7.0.2",
"rollup-plugin-visualizer": "5.12.0", "rollup-plugin-visualizer": "5.12.0",
"serve-handler": "6.1.5", "serve-handler": "6.1.5",
"sinon": "18.0.0", "sinon": "18.0.0",
"source-map-url": "0.4.1",
"systemjs": "6.15.1", "systemjs": "6.15.1",
"tar": "7.4.3", "tar": "7.4.0",
"terser-webpack-plugin": "5.3.10", "terser-webpack-plugin": "5.3.10",
"transform-async-modules-webpack-plugin": "1.1.1", "transform-async-modules-webpack-plugin": "1.1.1",
"ts-lit-plugin": "2.0.2", "ts-lit-plugin": "2.0.2",
"typescript": "5.5.4", "typescript": "5.5.3",
"webpack": "5.94.0", "webpack": "5.92.1",
"webpack-cli": "5.1.4", "webpack-cli": "5.1.4",
"webpack-dev-server": "5.0.4", "webpack-dev-server": "5.0.4",
"webpack-manifest-plugin": "5.0.0", "webpack-manifest-plugin": "5.0.0",
"webpack-stats-plugin": "1.1.3", "webpack-stats-plugin": "1.1.3",
"webpackbar": "6.0.1", "webpackbar": "6.0.1",
"workbox-build": "patch:workbox-build@npm%3A7.1.1#~/.yarn/patches/workbox-build-npm-7.1.1-a854f3faae.patch" "workbox-build": "7.1.1"
}, },
"_comment": "Polymer 3.2 contained a bug, fixed in https://github.com/Polymer/polymer/pull/5569, add as patch", "_comment": "Polymer 3.2 contained a bug, fixed in https://github.com/Polymer/polymer/pull/5569, add as patch",
"resolutions": { "resolutions": {
@@ -254,9 +253,9 @@
"lit": "2.8.0", "lit": "2.8.0",
"clean-css": "5.3.3", "clean-css": "5.3.3",
"@lit/reactive-element": "1.6.3", "@lit/reactive-element": "1.6.3",
"@fullcalendar/daygrid": "6.1.15", "@fullcalendar/daygrid": "6.1.11",
"sortablejs@1.15.2": "patch:sortablejs@npm%3A1.15.2#~/.yarn/patches/sortablejs-npm-1.15.2-73347ae85a.patch", "sortablejs@1.15.2": "patch:sortablejs@npm%3A1.15.2#~/.yarn/patches/sortablejs-npm-1.15.2-73347ae85a.patch",
"leaflet-draw@1.0.4": "patch:leaflet-draw@npm%3A1.0.4#./.yarn/patches/leaflet-draw-npm-1.0.4-0ca0ebcf65.patch" "leaflet-draw@1.0.4": "patch:leaflet-draw@npm%3A1.0.4#./.yarn/patches/leaflet-draw-npm-1.0.4-0ca0ebcf65.patch"
}, },
"packageManager": "yarn@4.4.0" "packageManager": "yarn@4.3.1"
} }

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 5.2 KiB

View File

@@ -1,66 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg id="b" data-name="Layer_2" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 760.69 138.69">
<g id="c" data-name="Layer_1">
<g>
<g>
<path d="M136.22,68.35c3.31-.05,6.29.72,8.92,2.31s4.67,3.77,6.11,6.55,2.14,5.89,2.11,9.33c.03,3.44-.68,6.55-2.12,9.34s-3.49,4.98-6.13,6.56-5.62,2.36-8.93,2.31c-3.33,0-6.29-.78-8.88-2.33s-4.6-3.71-6.02-6.48-2.13-5.9-2.13-9.4c-.03-2.56.38-4.98,1.22-7.24s2.01-4.21,3.5-5.82,3.31-2.88,5.45-3.8,4.45-1.36,6.91-1.32ZM136.27,98.35c3.09,0,5.56-1.07,7.41-3.2s2.77-5,2.77-8.61-.91-6.52-2.73-8.65-4.3-3.19-7.44-3.19-5.63,1.06-7.46,3.19-2.75,5.01-2.75,8.65.91,6.52,2.74,8.64,4.32,3.18,7.48,3.18Z"/>
<path d="M184.16,80.53c0,3.47-1.06,6.27-3.18,8.41s-4.98,3.21-8.59,3.21h-7.45v12h-6.56v-35.18h14.06c3.64,0,6.5,1.04,8.59,3.11s3.13,4.89,3.13,8.45ZM177.25,80.39c0-1.64-.52-2.98-1.56-4.03s-2.52-1.57-4.44-1.57h-6.3v11.65h6.26c1.95,0,3.45-.55,4.49-1.65s1.56-2.57,1.56-4.39Z"/>
<path d="M210.82,98.02v6.14h-22.03v-35.18h21.98v6.19h-15.42v8.3h13.78v5.81h-13.78v8.74h15.47Z"/>
<path d="M246.95,68.98v35.18h-6.49l-16.08-24.77v24.77h-6.52v-35.18h6.52l16.08,24.77v-24.77h6.49Z"/>
<path d="M266.45,68.98h6.56v14.44l14.7.05v-14.48h6.63v35.18h-6.63v-14.84l-14.7-.09v14.93h-6.56v-35.18Z"/>
<path d="M316.41,68.35c3.31-.05,6.29.72,8.92,2.31s4.67,3.77,6.11,6.55,2.14,5.89,2.11,9.33c.03,3.44-.68,6.55-2.12,9.34s-3.49,4.98-6.13,6.56-5.62,2.36-8.93,2.31c-3.33,0-6.29-.78-8.88-2.33s-4.6-3.71-6.02-6.48-2.13-5.9-2.13-9.4c-.03-2.56.38-4.98,1.22-7.24s2.01-4.21,3.5-5.82,3.31-2.88,5.45-3.8,4.45-1.36,6.91-1.32ZM316.46,98.35c3.09,0,5.56-1.07,7.41-3.2s2.77-5,2.77-8.61-.91-6.52-2.73-8.65-4.3-3.19-7.44-3.19-5.63,1.06-7.46,3.19-2.75,5.01-2.75,8.65.91,6.52,2.74,8.64,4.32,3.18,7.48,3.18Z"/>
<path d="M373.66,68.98v35.18h-6.45v-20.55l-8.11,20.55h-6.23l-8.02-20.39v20.39h-6.28v-35.18h6.28l11.13,27.54,11.23-27.54h6.45Z"/>
<path d="M402.87,98.02v6.14h-22.03v-35.18h21.98v6.19h-15.42v8.3h13.78v5.81h-13.78v8.74h15.47Z"/>
<path d="M427.83,75.12v8.93h13.01l-.05,5.91h-12.96v14.2h-6.52v-35.18h21.98l-.05,6.14h-15.42Z"/>
<path d="M463.16,68.35c3.31-.05,6.29.72,8.92,2.31s4.67,3.77,6.11,6.55,2.14,5.89,2.11,9.33c.03,3.44-.68,6.55-2.12,9.34s-3.49,4.98-6.13,6.56-5.62,2.36-8.93,2.31c-3.33,0-6.29-.78-8.88-2.33s-4.6-3.71-6.02-6.48-2.13-5.9-2.13-9.4c-.03-2.56.38-4.98,1.22-7.24s2.01-4.21,3.5-5.82,3.31-2.88,5.45-3.8,4.45-1.36,6.91-1.32ZM463.21,98.35c3.09,0,5.56-1.07,7.41-3.2s2.77-5,2.77-8.61-.91-6.52-2.73-8.65-4.3-3.19-7.44-3.19-5.63,1.06-7.46,3.19-2.75,5.01-2.75,8.65.91,6.52,2.74,8.64,4.32,3.18,7.48,3.18Z"/>
<path d="M485,68.98h6.56v22.12c0,2.31.72,4.12,2.16,5.43s3.3,1.96,5.58,1.96,4.08-.67,5.58-2.02,2.25-3.13,2.25-5.37v-22.12h6.52v22.31c0,2.08-.38,3.98-1.14,5.7s-1.79,3.14-3.09,4.25-2.82,1.98-4.56,2.59-3.59.91-5.55.91c-2.59,0-4.96-.52-7.1-1.55s-3.88-2.58-5.2-4.65-1.99-4.49-1.99-7.25v-22.31Z"/>
<path d="M549.63,68.98v35.18h-6.49l-16.08-24.77v24.77h-6.52v-35.18h6.52l16.08,24.77v-24.77h6.49Z"/>
<path d="M586.9,86.58c.05,3.34-.71,6.37-2.27,9.08s-3.7,4.82-6.42,6.32-5.73,2.23-9.02,2.18h-12.42v-35.18h12.42c2.45-.03,4.78.39,6.98,1.28s4.1,2.1,5.68,3.66,2.84,3.43,3.75,5.64,1.35,4.55,1.3,7.03ZM579.99,86.58c0-3.39-1-6.16-3.01-8.3s-4.62-3.21-7.84-3.21h-5.81v23.04h5.81c3.27,0,5.89-1.06,7.88-3.19s2.98-4.91,2.98-8.34Z"/>
<path d="M609.16,96.19h-12.73l-2.79,7.97h-6.82l12.68-35.18h6.63l12.66,35.18h-6.96l-2.67-7.97ZM607.24,90.73l-4.43-12.87-4.45,12.87h8.88Z"/>
<path d="M642.87,75.17h-9.89v28.99h-6.56v-28.99h-9.94v-6.19h26.39v6.19Z"/>
<path d="M647.06,104.16v-35.18h6.56v35.18h-6.56Z"/>
<path d="M675.71,68.35c3.31-.05,6.29.72,8.92,2.31s4.67,3.77,6.11,6.55,2.14,5.89,2.11,9.33c.03,3.44-.68,6.55-2.12,9.34s-3.49,4.98-6.13,6.56-5.62,2.36-8.93,2.31c-3.33,0-6.29-.78-8.88-2.33s-4.6-3.71-6.02-6.48-2.13-5.9-2.13-9.4c-.03-2.56.38-4.98,1.22-7.24s2.01-4.21,3.5-5.82,3.31-2.88,5.45-3.8,4.45-1.36,6.91-1.32ZM675.76,98.35c3.09,0,5.56-1.07,7.41-3.2s2.77-5,2.77-8.61-.91-6.52-2.73-8.65-4.3-3.19-7.44-3.19-5.63,1.06-7.46,3.19-2.75,5.01-2.75,8.65.91,6.52,2.74,8.64,4.32,3.18,7.48,3.18Z"/>
<path d="M726.96,68.98v35.18h-6.49l-16.08-24.77v24.77h-6.52v-35.18h6.52l16.08,24.77v-24.77h6.49Z"/>
</g>
<g>
<path d="M94.34,79.34c0,2.75-2.25,5-5,5h-50c-2.75,0-5-2.25-5-5v-20c0-2.75,1.59-6.59,3.54-8.54l22.93-22.93c1.94-1.94,5.13-1.94,7.07,0l22.93,22.93c1.94,1.94,3.54,5.79,3.54,8.54v20Z"/>
<g>
<rect x="34.34" y="94.34" width="60" height="10" rx="2.5" ry="2.5"/>
<rect x="34.34" y="94.34" width="10" height="20" rx="1.56" ry="1.56"/>
<rect x="84.34" y="94.34" width="10" height="20" rx="1.56" ry="1.56"/>
</g>
</g>
<path d="M735.34,3c12.32,0,22.34,10.02,22.34,22.34v88c0,12.32-10.02,22.34-22.34,22.34H25.34c-12.32,0-22.34-10.02-22.34-22.34V25.34C3,13.02,13.02,3,25.34,3h710M735.34,0H25.34C11.37,0,0,11.37,0,25.34v88c0,13.98,11.37,25.34,25.34,25.34h710c13.97,0,25.34-11.37,25.34-25.34V25.34c0-13.98-11.37-25.34-25.34-25.34h0Z"/>
<g>
<path d="M120.98,36.79h2.95v7.26l7.66.02v-7.29h2.97v17.37h-2.97v-7.47l-7.66-.02v7.49h-2.95v-17.37Z"/>
<path d="M146.97,36.47c1.63,0,3.09.39,4.37,1.16s2.28,1.84,2.99,3.2,1.06,2.9,1.06,4.61c.02,1.7-.32,3.24-1.04,4.62s-1.72,2.47-3.02,3.25-2.75,1.16-4.36,1.14c-1.62.02-3.08-.36-4.37-1.14s-2.29-1.86-3-3.24-1.05-2.91-1.03-4.61c0-1.27.2-2.47.61-3.58s.99-2.08,1.72-2.88,1.63-1.42,2.68-1.88,2.18-.67,3.39-.66ZM146.99,51.57c1.6,0,2.89-.56,3.85-1.67s1.45-2.6,1.45-4.45-.48-3.32-1.45-4.43-2.25-1.66-3.85-1.66-2.89.55-3.86,1.66-1.45,2.58-1.45,4.43.48,3.34,1.44,4.46,2.25,1.67,3.88,1.67Z"/>
<path d="M176.51,36.79v17.37h-2.89v-10.78l-4.29,10.78h-2.81l-4.25-10.71v10.71h-2.84v-17.37h2.84l5.66,13.92,5.69-13.92h2.89Z"/>
<path d="M192.41,51.37v2.79h-10.78v-17.37h10.78v2.81h-7.83v4.5h7v2.61h-7v4.66h7.83Z"/>
<path d="M213.93,50.11h-6.61l-1.43,4.04h-3.04l6.27-17.37h3.04l6.29,17.37h-3.11l-1.41-4.04ZM213.07,47.62l-2.43-6.95-2.45,6.95h4.88Z"/>
<path d="M226.96,36.47c1.59,0,2.91.39,3.96,1.16s1.7,1.81,1.94,3.1l-2.78.76c-.16-.74-.52-1.32-1.09-1.72s-1.27-.6-2.11-.6c-.9,0-1.61.21-2.14.64s-.79,1-.79,1.71c0,1.12.7,1.85,2.09,2.18l2.84.71c1.46.38,2.56.98,3.29,1.81s1.09,1.84,1.09,3.05c0,1.55-.56,2.8-1.68,3.76s-2.63,1.44-4.51,1.44c-1.7,0-3.13-.4-4.3-1.2-1.15-.83-1.84-1.92-2.05-3.28l2.78-.72c.1.77.48,1.37,1.14,1.8s1.5.65,2.53.65,1.76-.21,2.32-.62.84-.98.84-1.69c0-1.12-.7-1.85-2.09-2.21l-2.84-.69c-1.46-.33-2.55-.92-3.28-1.77s-1.1-1.88-1.1-3.11c0-1.53.54-2.78,1.63-3.74s2.52-1.44,4.29-1.44Z"/>
<path d="M242.38,36.47c1.59,0,2.91.39,3.96,1.16s1.7,1.81,1.94,3.1l-2.78.76c-.16-.74-.52-1.32-1.09-1.72s-1.27-.6-2.11-.6c-.9,0-1.61.21-2.14.64s-.79,1-.79,1.71c0,1.12.7,1.85,2.09,2.18l2.84.71c1.46.38,2.56.98,3.29,1.81s1.09,1.84,1.09,3.05c0,1.55-.56,2.8-1.68,3.76s-2.63,1.44-4.51,1.44c-1.7,0-3.13-.4-4.3-1.2-1.15-.83-1.84-1.92-2.05-3.28l2.78-.72c.1.77.48,1.37,1.14,1.8s1.5.65,2.53.65,1.76-.21,2.32-.62.84-.98.84-1.69c0-1.12-.7-1.85-2.09-2.21l-2.84-.69c-1.46-.33-2.55-.92-3.28-1.77s-1.1-1.88-1.1-3.11c0-1.53.54-2.78,1.63-3.74s2.52-1.44,4.29-1.44Z"/>
<path d="M252.68,54.16v-17.37h2.95v17.37h-2.95Z"/>
<path d="M265.82,36.47c1.59,0,2.91.39,3.96,1.16s1.7,1.81,1.94,3.1l-2.78.76c-.16-.74-.52-1.32-1.09-1.72s-1.27-.6-2.11-.6c-.9,0-1.61.21-2.14.64s-.79,1-.79,1.71c0,1.12.7,1.85,2.09,2.18l2.84.71c1.46.38,2.56.98,3.29,1.81s1.09,1.84,1.09,3.05c0,1.55-.56,2.8-1.68,3.76s-2.63,1.44-4.51,1.44c-1.7,0-3.13-.4-4.3-1.2-1.15-.83-1.84-1.92-2.05-3.28l2.78-.72c.1.77.48,1.37,1.14,1.8s1.5.65,2.53.65,1.76-.21,2.32-.62.84-.98.84-1.69c0-1.12-.7-1.85-2.09-2.21l-2.84-.69c-1.46-.33-2.55-.92-3.28-1.77s-1.1-1.88-1.1-3.11c0-1.53.54-2.78,1.63-3.74s2.52-1.44,4.29-1.44Z"/>
<path d="M287.47,39.57h-4.97v14.58h-2.95v-14.58h-4.97v-2.79h12.9v2.79Z"/>
<path d="M298.87,50.11h-6.61l-1.43,4.04h-3.04l6.27-17.37h3.04l6.29,17.37h-3.11l-1.41-4.04ZM298.01,47.62l-2.43-6.95-2.45,6.95h4.88Z"/>
<path d="M320.89,36.79v17.37h-2.93l-8.25-12.67v12.67h-2.93v-17.37h2.93l8.25,12.65v-12.65h2.93Z"/>
<path d="M337.31,39.57h-4.97v14.58h-2.95v-14.58h-4.97v-2.79h12.9v2.79Z"/>
<path d="M348.75,54.16v-17.14h2.05v17.14h-2.05Z"/>
<path d="M360.95,36.72c1.55,0,2.82.38,3.81,1.14,1,.74,1.61,1.72,1.82,2.95l-1.95.52c-.16-.87-.56-1.54-1.23-2.02s-1.5-.71-2.5-.71c-1.08,0-1.95.27-2.6.8s-.97,1.24-.97,2.13c0,1.36.84,2.26,2.52,2.71l2.9.73c1.37.34,2.41.9,3.11,1.68s1.05,1.74,1.05,2.9c0,1.46-.53,2.64-1.6,3.54s-2.49,1.36-4.28,1.36c-1.61,0-2.95-.37-4.03-1.12s-1.72-1.76-1.95-3.06l1.98-.55c.13.88.55,1.56,1.25,2.06s1.63.75,2.77.75,2.12-.26,2.79-.77,1.02-1.22,1.02-2.14c0-1.44-.84-2.36-2.52-2.77l-2.88-.71c-1.38-.34-2.42-.9-3.12-1.69s-1.05-1.75-1.05-2.9c0-1.44.52-2.61,1.56-3.51s2.4-1.35,4.09-1.35Z"/>
<path d="M388.35,49.75h-7.54l-1.59,4.4h-2.07l6.25-17.14h2.36l6.31,17.14h-2.15l-1.57-4.4ZM387.73,48.05l-3.09-8.71-3.2,8.71h6.29Z"/>
<path d="M415.46,42.47c0,1.6-.5,2.91-1.5,3.95s-2.32,1.56-3.97,1.56h-4.53v6.18h-2.05v-17.14h6.6c1.67,0,3,.49,3.98,1.47s1.47,2.31,1.47,3.98ZM413.31,42.42c0-1.07-.32-1.92-.95-2.56s-1.51-.96-2.64-.96h-4.26v7.24h4.17c1.15,0,2.06-.34,2.71-1.02s.98-1.58.98-2.7Z"/>
<path d="M428.37,46.9l3.43,7.26h-2.31l-3.18-6.95h-4.76v6.95h-2.05v-17.14h6.54c1.81,0,3.22.45,4.24,1.35s1.53,2.14,1.53,3.72c0,1.22-.3,2.26-.9,3.1s-1.44,1.41-2.53,1.7ZM429.64,42.12c0-1.01-.32-1.81-.95-2.38s-1.52-.86-2.66-.86h-4.5v6.47h4.53c1.15,0,2.03-.28,2.64-.85s.92-1.36.92-2.38Z"/>
<path d="M443.34,36.74c1.18-.02,2.28.2,3.31.65s1.9,1.07,2.62,1.85,1.28,1.73,1.69,2.83.6,2.27.59,3.52c.02,1.67-.33,3.19-1.03,4.54s-1.68,2.42-2.95,3.19-2.68,1.14-4.25,1.12c-1.59,0-3-.38-4.25-1.13s-2.21-1.81-2.89-3.15-1.03-2.87-1.03-4.57c-.02-1.67.32-3.18,1.02-4.53s1.67-2.42,2.93-3.2,2.68-1.15,4.24-1.13ZM443.34,52.45c1.8,0,3.26-.64,4.38-1.91s1.68-2.92,1.68-4.95-.56-3.71-1.68-4.98-2.58-1.9-4.38-1.9-3.28.63-4.4,1.9-1.68,2.93-1.68,4.98.56,3.69,1.68,4.96,2.59,1.9,4.39,1.9Z"/>
<path d="M464.3,37.02v12.42c0,1.49-.47,2.71-1.41,3.64s-2.17,1.39-3.71,1.39c-1.56,0-2.76-.46-3.61-1.37s-1.27-2.13-1.27-3.69v-.55h1.98v.55c0,1.18.29,1.99.86,2.45.59.45,1.26.67,2.02.67.93,0,1.68-.26,2.24-.78.57-.52.85-1.32.85-2.39v-12.35h2.05Z"/>
<path d="M479.86,52.23v1.93h-10.35v-17.14h10.33v1.95h-8.31v5.67h7.53v1.8h-7.53v5.79h8.33Z"/>
<path d="M496.97,42.42c-.36-1.15-1.01-2.06-1.93-2.71s-2.02-.98-3.3-.98c-1.79,0-3.24.63-4.35,1.89s-1.66,2.92-1.66,4.96.55,3.72,1.66,4.96,2.55,1.86,4.33,1.86c1.27,0,2.39-.31,3.35-.94.95-.63,1.61-1.44,1.98-2.45l1.93.83c-.55,1.41-1.48,2.53-2.78,3.36-1.31.81-2.81,1.22-4.51,1.22-2.4,0-4.35-.81-5.84-2.43s-2.24-3.75-2.24-6.4c0-1.74.34-3.28,1.02-4.62s1.64-2.39,2.88-3.13,2.65-1.11,4.25-1.11c1.8,0,3.33.46,4.59,1.38,1.26.91,2.12,2.1,2.57,3.59l-1.93.71Z"/>
<path d="M512.92,38.95h-5.12v15.21h-2.05v-15.21h-5.16v-1.93h12.33v1.93Z"/>
<path d="M536.4,49.66c0,1.39-.49,2.48-1.46,3.29s-2.27,1.21-3.9,1.21h-6.72v-17.12h6.58c1.65,0,2.95.41,3.89,1.24s1.41,1.93,1.41,3.32c0,.92-.21,1.72-.64,2.4s-1.02,1.2-1.79,1.57c.81.37,1.45.91,1.92,1.62s.7,1.53.7,2.47ZM526.3,38.81v5.97h4.54c1.03,0,1.85-.27,2.46-.81s.92-1.24.92-2.09c0-.91-.31-1.65-.93-2.22s-1.45-.85-2.5-.85h-4.5ZM534.45,49.39c0-.91-.32-1.64-.95-2.17s-1.44-.8-2.46-.8h-4.74v5.95h4.74c1.04,0,1.86-.27,2.48-.81s.92-1.26.92-2.16Z"/>
<path d="M541.07,37.02l4.54,8.1,4.54-8.1h2.24l-5.76,10.05v7.09h-2.05v-7.09l-5.83-10.05h2.31Z"/>
<path d="M574.27,38.95h-5.12v15.21h-2.05v-15.21h-5.16v-1.93h12.33v1.93Z"/>
<path d="M577.95,37.02h2.05v7.55l8.74.02v-7.58h2.05v17.14h-2.05v-7.76l-8.74-.02v7.79h-2.05v-17.14Z"/>
<path d="M606.55,52.23v1.93h-10.35v-17.14h10.33v1.95h-8.31v5.67h7.53v1.8h-7.53v5.79h8.33Z"/>
</g>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 12 KiB

View File

@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
[project] [project]
name = "home-assistant-frontend" name = "home-assistant-frontend"
version = "20240809.0" version = "20240705.0"
license = {text = "Apache-2.0"} license = {text = "Apache-2.0"}
description = "The Home Assistant frontend" description = "The Home Assistant frontend"
readme = "README.md" readme = "README.md"

View File

@@ -37,7 +37,8 @@
{ {
"description": "Group tsparticles engine and presets", "description": "Group tsparticles engine and presets",
"groupName": "tsparticles", "groupName": "tsparticles",
"matchPackageNames": ["tsparticles-engine", "tsparticles-preset-{/,}**"] "matchPackageNames": ["tsparticles-engine"],
"matchPackagePrefixes": ["tsparticles-preset-"]
}, },
{ {
"description": "Group date-fns with dependent timezone package", "description": "Group date-fns with dependent timezone package",
@@ -47,8 +48,8 @@
{ {
"description": "Group and temporarily disable WDS packages", "description": "Group and temporarily disable WDS packages",
"groupName": "Web Dev Server", "groupName": "Web Dev Server",
"enabled": false, "matchPackagePrefixes": ["@web/dev-server"],
"matchPackageNames": ["@web/dev-server{/,}**"] "enabled": false
} }
] ]
} }

View File

@@ -40,6 +40,7 @@ import {
mdiImageFilterFrames, mdiImageFilterFrames,
mdiLightbulb, mdiLightbulb,
mdiLightningBolt, mdiLightningBolt,
mdiMailbox,
mdiMapMarkerRadius, mdiMapMarkerRadius,
mdiMeterGas, mdiMeterGas,
mdiMicrophoneMessage, mdiMicrophoneMessage,
@@ -118,6 +119,7 @@ export const FIXED_DOMAIN_ICONS = {
input_text: mdiFormTextbox, input_text: mdiFormTextbox,
lawn_mower: mdiRobotMower, lawn_mower: mdiRobotMower,
light: mdiLightbulb, light: mdiLightbulb,
mailbox: mdiMailbox,
notify: mdiCommentAlert, notify: mdiCommentAlert,
number: mdiRayVertex, number: mdiRayVertex,
persistent_notification: mdiBell, persistent_notification: mdiBell,

View File

@@ -26,7 +26,7 @@ export const FIXED_DOMAIN_STATES = {
humidifier: ["on", "off"], humidifier: ["on", "off"],
input_boolean: ["on", "off"], input_boolean: ["on", "off"],
input_button: [], input_button: [],
lawn_mower: ["error", "paused", "mowing", "returning", "docked"], lawn_mower: ["error", "paused", "mowing", "docked"],
light: ["on", "off"], light: ["on", "off"],
lock: [ lock: [
"jammed", "jammed",
@@ -125,7 +125,6 @@ const FIXED_DOMAIN_ATTRIBUTE_STATES = {
"off", "off",
"idle", "idle",
"preheating", "preheating",
"defrosting",
"heating", "heating",
"cooling", "cooling",
"drying", "drying",

View File

@@ -25,9 +25,7 @@ export const navigate = (path: string, options?: NavigateOptions) => {
if (__DEMO__) { if (__DEMO__) {
if (replace) { if (replace) {
mainWindow.history.replaceState( mainWindow.history.replaceState(
mainWindow.history.state?.root mainWindow.history.state?.root ? { root: true } : options?.data ?? null,
? { root: true }
: (options?.data ?? null),
"", "",
`${mainWindow.location.pathname}#${path}` `${mainWindow.location.pathname}#${path}`
); );
@@ -36,7 +34,7 @@ export const navigate = (path: string, options?: NavigateOptions) => {
} }
} else if (replace) { } else if (replace) {
mainWindow.history.replaceState( mainWindow.history.replaceState(
mainWindow.history.state?.root ? { root: true } : (options?.data ?? null), mainWindow.history.state?.root ? { root: true } : options?.data ?? null,
"", "",
path path
); );

View File

@@ -1,6 +1,5 @@
import { LitElement, TemplateResult, html } from "lit"; import { LitElement, TemplateResult, html } from "lit";
import { customElement, property } from "lit/decorators"; import { customElement, property } from "lit/decorators";
import { HassServiceTarget } from "home-assistant-js-websocket";
import { showConfirmationDialog } from "../../dialogs/generic/show-dialog-box"; import { showConfirmationDialog } from "../../dialogs/generic/show-dialog-box";
import "./ha-progress-button"; import "./ha-progress-button";
import { HomeAssistant } from "../../types"; import { HomeAssistant } from "../../types";
@@ -18,9 +17,7 @@ class HaCallServiceButton extends LitElement {
@property() public service!: string; @property() public service!: string;
@property({ type: Object }) public target!: HassServiceTarget; @property({ type: Object }) public serviceData = {};
@property({ type: Object }) public data = {};
@property() public confirmation?; @property() public confirmation?;
@@ -42,8 +39,7 @@ class HaCallServiceButton extends LitElement {
const eventData = { const eventData = {
domain: this.domain, domain: this.domain,
service: this.service, service: this.service,
data: this.data, serviceData: this.serviceData,
target: this.target,
success: false, success: false,
}; };
@@ -51,12 +47,7 @@ class HaCallServiceButton extends LitElement {
this.shadowRoot!.querySelector("ha-progress-button")!; this.shadowRoot!.querySelector("ha-progress-button")!;
try { try {
await this.hass.callService( await this.hass.callService(this.domain, this.service, this.serviceData);
this.domain,
this.service,
this.data,
this.target
);
this.progress = false; this.progress = false;
progressElement.actionSuccess(); progressElement.actionSuccess();
eventData.success = true; eventData.success = true;
@@ -94,8 +85,7 @@ declare global {
"hass-service-called": { "hass-service-called": {
domain: string; domain: string;
service: string; service: string;
target: HassServiceTarget; serviceData: object;
data: object;
success: boolean; success: boolean;
}; };
} }

View File

@@ -159,10 +159,10 @@ export class StateHistoryChartTimeline extends LitElement {
}, },
afterUpdate: (y) => { afterUpdate: (y) => {
const yWidth = this.showNames const yWidth = this.showNames
? (y.width ?? 0) ? y.width ?? 0
: computeRTL(this.hass) : computeRTL(this.hass)
? 0 ? 0
: (y.left ?? 0); : y.left ?? 0;
if ( if (
this._yWidth !== Math.floor(yWidth) && this._yWidth !== Math.floor(yWidth) &&
y.ticks.length === this.data.length y.ticks.length === this.data.length

View File

@@ -78,8 +78,6 @@ export class DialogDataTableSettings extends LitElement {
return nothing; return nothing;
} }
const localize = this._params.localizeFunc || this.hass.localize;
const columns = this._sortedColumns( const columns = this._sortedColumns(
this._params.columns, this._params.columns,
this._columnOrder, this._columnOrder,
@@ -92,7 +90,7 @@ export class DialogDataTableSettings extends LitElement {
@closed=${this.closeDialog} @closed=${this.closeDialog}
.heading=${createCloseHeading( .heading=${createCloseHeading(
this.hass, this.hass,
localize("ui.components.data-table.settings.header") this.hass.localize("ui.components.data-table.settings.header")
)} )}
> >
<ha-sortable <ha-sortable
@@ -109,8 +107,7 @@ export class DialogDataTableSettings extends LitElement {
const canHide = !col.main && col.hideable !== false; const canHide = !col.main && col.hideable !== false;
const isVisible = !(this._columnOrder && const isVisible = !(this._columnOrder &&
this._columnOrder.includes(col.key) this._columnOrder.includes(col.key)
? (this._hiddenColumns?.includes(col.key) ?? ? this._hiddenColumns?.includes(col.key) ?? col.defaultHidden
col.defaultHidden)
: col.defaultHidden); : col.defaultHidden);
return html`<ha-list-item return html`<ha-list-item
@@ -149,10 +146,12 @@ export class DialogDataTableSettings extends LitElement {
</mwc-list> </mwc-list>
</ha-sortable> </ha-sortable>
<ha-button slot="secondaryAction" @click=${this._reset} <ha-button slot="secondaryAction" @click=${this._reset}
>${localize("ui.components.data-table.settings.restore")}</ha-button >${this.hass.localize(
"ui.components.data-table.settings.restore"
)}</ha-button
> >
<ha-button slot="primaryAction" @click=${this.closeDialog}> <ha-button slot="primaryAction" @click=${this.closeDialog}>
${localize("ui.components.data-table.settings.done")} ${this.hass.localize("ui.components.data-table.settings.done")}
</ha-button> </ha-button>
</ha-dialog> </ha-dialog>
`; `;
@@ -194,7 +193,6 @@ export class DialogDataTableSettings extends LitElement {
.filter(([_key, col]) => col.defaultHidden) .filter(([_key, col]) => col.defaultHidden)
.map(([key]) => key)), .map(([key]) => key)),
]; ];
if (wasHidden && hidden.includes(column)) { if (wasHidden && hidden.includes(column)) {
hidden.splice(hidden.indexOf(column), 1); hidden.splice(hidden.indexOf(column), 1);
} else if (!wasHidden) { } else if (!wasHidden) {
@@ -244,11 +242,7 @@ export class DialogDataTableSettings extends LitElement {
newOrder.splice(lastMoveable + 1, 0, col.key); newOrder.splice(lastMoveable + 1, 0, col.key);
} }
if ( if (col.defaultHidden) {
col.key !== column &&
col.defaultHidden &&
!hidden.includes(col.key)
) {
hidden.push(col.key); hidden.push(col.key);
} }
} }

View File

@@ -85,9 +85,9 @@ export interface DataTableColumnData<T = any> extends DataTableSortColumnData {
| "flex"; | "flex";
template?: (row: T) => TemplateResult | string | typeof nothing; template?: (row: T) => TemplateResult | string | typeof nothing;
extraTemplate?: (row: T) => TemplateResult | string | typeof nothing; extraTemplate?: (row: T) => TemplateResult | string | typeof nothing;
minWidth?: string; width?: string;
maxWidth?: string; maxWidth?: string;
flex?: number; grows?: boolean;
forceLTR?: boolean; forceLTR?: boolean;
hidden?: boolean; hidden?: boolean;
} }
@@ -216,18 +216,6 @@ export class HaDataTable extends LitElement {
this.updateComplete.then(() => this._calcTableHeight()); this.updateComplete.then(() => this._calcTableHeight());
} }
protected updated() {
const header = this.renderRoot.querySelector(".mdc-data-table__header-row");
if (!header) {
return;
}
if (header.scrollWidth > header.clientWidth) {
this.style.setProperty("--table-row-width", `${header.scrollWidth}px`);
} else {
this.style.removeProperty("--table-row-width");
}
}
public willUpdate(properties: PropertyValues) { public willUpdate(properties: PropertyValues) {
super.willUpdate(properties); super.willUpdate(properties);
@@ -367,12 +355,7 @@ export class HaDataTable extends LitElement {
: `calc(100% - ${this._headerHeight}px)`, : `calc(100% - ${this._headerHeight}px)`,
})} })}
> >
<div <div class="mdc-data-table__header-row" role="row" aria-rowindex="1">
class="mdc-data-table__header-row"
role="row"
aria-rowindex="1"
@scroll=${this._scrollContent}
>
<slot name="header-row"> <slot name="header-row">
${this.selectable ${this.selectable
? html` ? html`
@@ -396,8 +379,7 @@ export class HaDataTable extends LitElement {
if ( if (
column.hidden || column.hidden ||
(this.columnOrder && this.columnOrder.includes(key) (this.columnOrder && this.columnOrder.includes(key)
? (this.hiddenColumns?.includes(key) ?? ? this.hiddenColumns?.includes(key) ?? column.defaultHidden
column.defaultHidden)
: column.defaultHidden) : column.defaultHidden)
) { ) {
return nothing; return nothing;
@@ -415,16 +397,18 @@ export class HaDataTable extends LitElement {
column.type === "overflow", column.type === "overflow",
sortable: Boolean(column.sortable), sortable: Boolean(column.sortable),
"not-sorted": Boolean(column.sortable && !sorted), "not-sorted": Boolean(column.sortable && !sorted),
grows: Boolean(column.grows),
}; };
return html` return html`
<div <div
aria-label=${ifDefined(column.label)} aria-label=${ifDefined(column.label)}
class="mdc-data-table__header-cell ${classMap(classes)}" class="mdc-data-table__header-cell ${classMap(classes)}"
style=${styleMap({ style=${column.width
minWidth: column.minWidth, ? styleMap({
maxWidth: column.maxWidth, [column.grows ? "minWidth" : "width"]: column.width,
flex: column.flex || 1, maxWidth: column.maxWidth || "",
})} })
: ""}
role="columnheader" role="columnheader"
aria-sort=${ifDefined( aria-sort=${ifDefined(
sorted sorted
@@ -534,7 +518,7 @@ export class HaDataTable extends LitElement {
(narrow && !column.main && !column.showNarrow) || (narrow && !column.main && !column.showNarrow) ||
column.hidden || column.hidden ||
(this.columnOrder && this.columnOrder.includes(key) (this.columnOrder && this.columnOrder.includes(key)
? (this.hiddenColumns?.includes(key) ?? column.defaultHidden) ? this.hiddenColumns?.includes(key) ?? column.defaultHidden
: column.defaultHidden) : column.defaultHidden)
) { ) {
return nothing; return nothing;
@@ -553,13 +537,15 @@ export class HaDataTable extends LitElement {
"mdc-data-table__cell--overflow-menu": "mdc-data-table__cell--overflow-menu":
column.type === "overflow-menu", column.type === "overflow-menu",
"mdc-data-table__cell--overflow": column.type === "overflow", "mdc-data-table__cell--overflow": column.type === "overflow",
grows: Boolean(column.grows),
forceLTR: Boolean(column.forceLTR), forceLTR: Boolean(column.forceLTR),
})}" })}"
style=${styleMap({ style=${column.width
minWidth: column.minWidth, ? styleMap({
maxWidth: column.maxWidth, [column.grows ? "minWidth" : "width"]: column.width,
flex: column.flex || 1, maxWidth: column.maxWidth ? column.maxWidth : "",
})} })
: ""}
> >
${column.template ${column.template
? column.template(row) ? column.template(row)
@@ -574,8 +560,8 @@ export class HaDataTable extends LitElement {
!column2.showNarrow && !column2.showNarrow &&
!(this.columnOrder && !(this.columnOrder &&
this.columnOrder.includes(key2) this.columnOrder.includes(key2)
? (this.hiddenColumns?.includes(key2) ?? ? this.hiddenColumns?.includes(key2) ??
column2.defaultHidden) column2.defaultHidden
: column2.defaultHidden) : column2.defaultHidden)
) )
.map( .map(
@@ -610,7 +596,7 @@ export class HaDataTable extends LitElement {
filteredData = await this._memFilterData( filteredData = await this._memFilterData(
this.data, this.data,
this._sortColumns, this._sortColumns,
this._filter.trim() this._filter
); );
} }
@@ -828,17 +814,6 @@ export class HaDataTable extends LitElement {
@eventOptions({ passive: true }) @eventOptions({ passive: true })
private _saveScrollPos(e: Event) { private _saveScrollPos(e: Event) {
this._savedScrollPos = (e.target as HTMLDivElement).scrollTop; this._savedScrollPos = (e.target as HTMLDivElement).scrollTop;
this.renderRoot.querySelector(".mdc-data-table__header-row")!.scrollLeft = (
e.target as HTMLDivElement
).scrollLeft;
}
@eventOptions({ passive: true })
private _scrollContent(e: Event) {
this.renderRoot.querySelector("lit-virtualizer")!.scrollLeft = (
e.target as HTMLDivElement
).scrollLeft;
} }
private _collapseGroup = (ev: Event) => { private _collapseGroup = (ev: Event) => {
@@ -913,8 +888,8 @@ export class HaDataTable extends LitElement {
.mdc-data-table__row { .mdc-data-table__row {
display: flex; display: flex;
width: 100%;
height: var(--data-table-row-height, 52px); height: var(--data-table-row-height, 52px);
width: var(--table-row-width, 100%);
} }
.mdc-data-table__row ~ .mdc-data-table__row { .mdc-data-table__row ~ .mdc-data-table__row {
@@ -938,26 +913,18 @@ export class HaDataTable extends LitElement {
.mdc-data-table__header-row { .mdc-data-table__header-row {
height: 56px; height: 56px;
display: flex; display: flex;
width: 100%;
border-bottom: 1px solid var(--divider-color); border-bottom: 1px solid var(--divider-color);
overflow: auto;
} }
/* Hide scrollbar for Chrome, Safari and Opera */
.mdc-data-table__header-row::-webkit-scrollbar { .mdc-data-table__header-row::-webkit-scrollbar {
display: none; display: none;
} }
/* Hide scrollbar for IE, Edge and Firefox */
.mdc-data-table__header-row {
-ms-overflow-style: none; /* IE and Edge */
scrollbar-width: none; /* Firefox */
}
.mdc-data-table__cell, .mdc-data-table__cell,
.mdc-data-table__header-cell { .mdc-data-table__header-cell {
padding-right: 16px; padding-right: 16px;
padding-left: 16px; padding-left: 16px;
min-width: 150px;
align-self: center; align-self: center;
overflow: hidden; overflow: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;
@@ -1005,8 +972,6 @@ export class HaDataTable extends LitElement {
letter-spacing: 0.0178571429em; letter-spacing: 0.0178571429em;
text-decoration: inherit; text-decoration: inherit;
text-transform: inherit; text-transform: inherit;
flex-grow: 0;
flex-shrink: 0;
} }
.mdc-data-table__cell a { .mdc-data-table__cell a {
@@ -1025,8 +990,7 @@ export class HaDataTable extends LitElement {
.mdc-data-table__header-cell--icon, .mdc-data-table__header-cell--icon,
.mdc-data-table__cell--icon { .mdc-data-table__cell--icon {
min-width: 64px; width: 54px;
flex: 0 0 64px !important;
} }
.mdc-data-table__cell--icon img { .mdc-data-table__cell--icon img {
@@ -1066,14 +1030,11 @@ export class HaDataTable extends LitElement {
.mdc-data-table__header-cell--overflow-menu, .mdc-data-table__header-cell--overflow-menu,
.mdc-data-table__header-cell--icon-button, .mdc-data-table__header-cell--icon-button,
.mdc-data-table__cell--icon-button { .mdc-data-table__cell--icon-button {
min-width: 64px;
flex: 0 0 64px !important;
padding: 8px; padding: 8px;
} }
.mdc-data-table__header-cell--icon-button, .mdc-data-table__header-cell--icon-button,
.mdc-data-table__cell--icon-button { .mdc-data-table__cell--icon-button {
min-width: 56px;
width: 56px; width: 56px;
} }

View File

@@ -1,5 +1,4 @@
import { fireEvent } from "../../common/dom/fire_event"; import { fireEvent } from "../../common/dom/fire_event";
import { LocalizeFunc } from "../../common/translations/localize";
import { DataTableColumnContainer } from "./ha-data-table"; import { DataTableColumnContainer } from "./ha-data-table";
export interface DataTableSettingsDialogParams { export interface DataTableSettingsDialogParams {
@@ -10,7 +9,6 @@ export interface DataTableSettingsDialogParams {
) => void; ) => void;
hiddenColumns?: string[]; hiddenColumns?: string[];
columnOrder?: string[]; columnOrder?: string[];
localizeFunc?: LocalizeFunc;
} }
export const loadDataTableSettingsDialog = () => export const loadDataTableSettingsDialog = () =>

View File

@@ -1,330 +0,0 @@
import { mdiDrag } from "@mdi/js";
import { HassEntity } from "home-assistant-js-websocket";
import { LitElement, PropertyValues, css, html, nothing } from "lit";
import { customElement, property, query, state } from "lit/decorators";
import { repeat } from "lit/directives/repeat";
import memoizeOne from "memoize-one";
import { ensureArray } from "../../common/array/ensure-array";
import { fireEvent } from "../../common/dom/fire_event";
import { computeDomain } from "../../common/entity/compute_domain";
import {
STATE_DISPLAY_SPECIAL_CONTENT,
STATE_DISPLAY_SPECIAL_CONTENT_DOMAINS,
} from "../../state-display/state-display";
import { HomeAssistant, ValueChangedEvent } from "../../types";
import "../ha-combo-box";
import type { HaComboBox } from "../ha-combo-box";
const HIDDEN_ATTRIBUTES = [
"access_token",
"available_modes",
"battery_icon",
"battery_level",
"code_arm_required",
"code_format",
"color_modes",
"device_class",
"editable",
"effect_list",
"entity_id",
"entity_picture",
"event_types",
"fan_modes",
"fan_speed_list",
"friendly_name",
"frontend_stream_type",
"has_date",
"has_time",
"hvac_modes",
"icon",
"id",
"max_color_temp_kelvin",
"max_mireds",
"max_temp",
"max",
"min_color_temp_kelvin",
"min_mireds",
"min_temp",
"min",
"mode",
"operation_list",
"options",
"percentage_step",
"precipitation_unit",
"preset_modes",
"pressure_unit",
"remaining",
"sound_mode_list",
"source_list",
"state_class",
"step",
"supported_color_modes",
"supported_features",
"swing_modes",
"target_temp_step",
"temperature_unit",
"token",
"unit_of_measurement",
"visibility_unit",
"wind_speed_unit",
];
@customElement("ha-entity-state-content-picker")
class HaEntityStatePicker extends LitElement {
@property({ attribute: false }) public hass!: HomeAssistant;
@property({ attribute: false }) public entityId?: string;
@property({ type: Boolean }) public autofocus = false;
@property({ type: Boolean }) public disabled = false;
@property({ type: Boolean }) public required = false;
@property({ type: Boolean, attribute: "allow-name" }) public allowName =
false;
@property() public label?: string;
@property() public value?: string[] | string;
@property() public helper?: string;
@state() private _opened = false;
@query("ha-combo-box", true) private _comboBox!: HaComboBox;
protected shouldUpdate(changedProps: PropertyValues) {
return !(!changedProps.has("_opened") && this._opened);
}
private options = memoizeOne(
(entityId?: string, stateObj?: HassEntity, allowName?: boolean) => {
const domain = entityId ? computeDomain(entityId) : undefined;
return [
{
label: this.hass.localize("ui.components.state-content-picker.state"),
value: "state",
},
...(allowName
? [
{
label: this.hass.localize(
"ui.components.state-content-picker.name"
),
value: "name",
},
]
: []),
{
label: this.hass.localize(
"ui.components.state-content-picker.last_changed"
),
value: "last_changed",
},
{
label: this.hass.localize(
"ui.components.state-content-picker.last_updated"
),
value: "last_updated",
},
...(domain
? STATE_DISPLAY_SPECIAL_CONTENT.filter((content) =>
STATE_DISPLAY_SPECIAL_CONTENT_DOMAINS[domain]?.includes(content)
).map((content) => ({
label: this.hass.localize(
`ui.components.state-content-picker.${content}`
),
value: content,
}))
: []),
...Object.keys(stateObj?.attributes ?? {})
.filter((a) => !HIDDEN_ATTRIBUTES.includes(a))
.map((attribute) => ({
value: attribute,
label: this.hass.formatEntityAttributeName(stateObj!, attribute),
})),
];
}
);
private _filter = "";
protected render() {
if (!this.hass) {
return nothing;
}
const value = this._value;
const stateObj = this.entityId
? this.hass.states[this.entityId]
: undefined;
const options = this.options(this.entityId, stateObj, this.allowName);
const optionItems = options.filter(
(option) => !this._value.includes(option.value)
);
return html`
${value?.length
? html`
<ha-sortable
no-style
@item-moved=${this._moveItem}
.disabled=${this.disabled}
>
<ha-chip-set>
${repeat(
this._value,
(item) => item,
(item, idx) => {
const label =
options.find((option) => option.value === item)?.label ||
item;
return html`
<ha-input-chip
.idx=${idx}
@remove=${this._removeItem}
.label=${label}
selected
>
<ha-svg-icon
slot="icon"
.path=${mdiDrag}
data-handle
></ha-svg-icon>
${label}
</ha-input-chip>
`;
}
)}
</ha-chip-set>
</ha-sortable>
`
: nothing}
<ha-combo-box
item-value-path="value"
item-label-path="label"
.hass=${this.hass}
.label=${this.label}
.helper=${this.helper}
.disabled=${this.disabled}
.required=${this.required && !value.length}
.value=${""}
.items=${optionItems}
allow-custom-value
@filter-changed=${this._filterChanged}
@value-changed=${this._comboBoxValueChanged}
@opened-changed=${this._openedChanged}
></ha-combo-box>
`;
}
private get _value() {
return !this.value ? [] : ensureArray(this.value);
}
private _openedChanged(ev: ValueChangedEvent<boolean>) {
this._opened = ev.detail.value;
this._comboBox.filteredItems = this._comboBox.items;
}
private _filterChanged(ev?: CustomEvent): void {
this._filter = ev?.detail.value || "";
const filteredItems = this._comboBox.items?.filter((item) => {
const label = item.label || item.value;
return label.toLowerCase().includes(this._filter?.toLowerCase());
});
if (this._filter) {
filteredItems?.unshift({ label: this._filter, value: this._filter });
}
this._comboBox.filteredItems = filteredItems;
}
private async _moveItem(ev: CustomEvent) {
ev.stopPropagation();
const { oldIndex, newIndex } = ev.detail;
const value = this._value;
const newValue = value.concat();
const element = newValue.splice(oldIndex, 1)[0];
newValue.splice(newIndex, 0, element);
this._setValue(newValue);
await this.updateComplete;
this._filterChanged();
}
private async _removeItem(ev) {
ev.stopPropagation();
const value: string[] = [...this._value];
value.splice(ev.target.idx, 1);
this._setValue(value);
await this.updateComplete;
this._filterChanged();
}
private _comboBoxValueChanged(ev: CustomEvent): void {
ev.stopPropagation();
const newValue = ev.detail.value;
if (this.disabled || newValue === "") {
return;
}
const currentValue = this._value;
if (currentValue.includes(newValue)) {
return;
}
setTimeout(() => {
this._filterChanged();
this._comboBox.setInputValue("");
}, 0);
this._setValue([...currentValue, newValue]);
}
private _setValue(value: string[]) {
const newValue =
value.length === 0 ? undefined : value.length === 1 ? value[0] : value;
this.value = newValue;
fireEvent(this, "value-changed", {
value: newValue,
});
}
static styles = css`
:host {
position: relative;
}
ha-chip-set {
padding: 8px 0;
}
.sortable-fallback {
display: none;
opacity: 0;
}
.sortable-ghost {
opacity: 0.4;
}
.sortable-drag {
cursor: grabbing;
}
`;
}
declare global {
interface HTMLElementTagNameMap {
"ha-entity-state-content-picker": HaEntityStatePicker;
}
}

View File

@@ -134,7 +134,7 @@ export class HaStateLabelBadge extends LitElement {
this._timerTimeRemaining this._timerTimeRemaining
)} )}
.description=${this.showName .description=${this.showName
? (this.name ?? computeStateName(entityState)) ? this.name ?? computeStateName(entityState)
: undefined} : undefined}
> >
${!image && showIcon ${!image && showIcon

View File

@@ -279,8 +279,6 @@ export class HaAreaPicker extends LitElement {
icon: null, icon: null,
aliases: [], aliases: [],
labels: [], labels: [],
created_at: 0,
modified_at: 0,
}, },
]; ];
} }
@@ -297,8 +295,6 @@ export class HaAreaPicker extends LitElement {
icon: "mdi:plus", icon: "mdi:plus",
aliases: [], aliases: [],
labels: [], labels: [],
created_at: 0,
modified_at: 0,
}, },
]; ];
} }
@@ -381,8 +377,6 @@ export class HaAreaPicker extends LitElement {
picture: null, picture: null,
labels: [], labels: [],
aliases: [], aliases: [],
created_at: 0,
modified_at: 0,
}, },
] as AreaRegistryEntry[]; ] as AreaRegistryEntry[];
} else { } else {
@@ -399,8 +393,6 @@ export class HaAreaPicker extends LitElement {
picture: null, picture: null,
labels: [], labels: [],
aliases: [], aliases: [],
created_at: 0,
modified_at: 0,
}, },
] as AreaRegistryEntry[]; ] as AreaRegistryEntry[];
} }

View File

@@ -1,12 +1,10 @@
import "@material/mwc-list/mwc-list-item"; import "@material/mwc-list/mwc-list-item";
import { css, html, LitElement, TemplateResult, nothing } from "lit"; import { css, html, LitElement, TemplateResult } from "lit";
import { customElement, property } from "lit/decorators"; import { customElement, property } from "lit/decorators";
import { mdiClose } from "@mdi/js";
import { ifDefined } from "lit/directives/if-defined"; import { ifDefined } from "lit/directives/if-defined";
import { fireEvent } from "../common/dom/fire_event"; import { fireEvent } from "../common/dom/fire_event";
import { stopPropagation } from "../common/dom/stop_propagation"; import { stopPropagation } from "../common/dom/stop_propagation";
import "./ha-select"; import "./ha-select";
import "./ha-icon-button";
import { HaTextField } from "./ha-textfield"; import { HaTextField } from "./ha-textfield";
import "./ha-input-helper-text"; import "./ha-input-helper-text";
@@ -126,128 +124,116 @@ export class HaBaseTimeInput extends LitElement {
*/ */
@property() amPm: "AM" | "PM" = "AM"; @property() amPm: "AM" | "PM" = "AM";
@property({ type: Boolean, reflect: true }) public clearable?: boolean;
protected render(): TemplateResult { protected render(): TemplateResult {
return html` return html`
${this.label ${this.label
? html`<label>${this.label}${this.required ? " *" : ""}</label>` ? html`<label>${this.label}${this.required ? " *" : ""}</label>`
: ""} : ""}
<div class="time-input-wrap-wrap"> <div class="time-input-wrap">
<div class="time-input-wrap"> ${this.enableDay
${this.enableDay ? html`
? html` <ha-textfield
<ha-textfield id="day"
id="day"
type="number"
inputmode="numeric"
.value=${this.days.toFixed()}
.label=${this.dayLabel}
name="days"
@change=${this._valueChanged}
@focusin=${this._onFocus}
no-spinner
.required=${this.required}
.autoValidate=${this.autoValidate}
min="0"
.disabled=${this.disabled}
suffix=":"
class="hasSuffix"
>
</ha-textfield>
`
: ""}
<ha-textfield
id="hour"
type="number"
inputmode="numeric"
.value=${this.hours.toFixed()}
.label=${this.hourLabel}
name="hours"
@change=${this._valueChanged}
@focusin=${this._onFocus}
no-spinner
.required=${this.required}
.autoValidate=${this.autoValidate}
maxlength="2"
max=${ifDefined(this._hourMax)}
min="0"
.disabled=${this.disabled}
suffix=":"
class="hasSuffix"
>
</ha-textfield>
<ha-textfield
id="min"
type="number"
inputmode="numeric"
.value=${this._formatValue(this.minutes)}
.label=${this.minLabel}
@change=${this._valueChanged}
@focusin=${this._onFocus}
name="minutes"
no-spinner
.required=${this.required}
.autoValidate=${this.autoValidate}
maxlength="2"
max="59"
min="0"
.disabled=${this.disabled}
.suffix=${this.enableSecond ? ":" : ""}
class=${this.enableSecond ? "has-suffix" : ""}
>
</ha-textfield>
${this.enableSecond
? html`<ha-textfield
id="sec"
type="number" type="number"
inputmode="numeric" inputmode="numeric"
.value=${this._formatValue(this.seconds)} .value=${this.days.toFixed()}
.label=${this.secLabel} .label=${this.dayLabel}
name="days"
@change=${this._valueChanged} @change=${this._valueChanged}
@focusin=${this._onFocus} @focusin=${this._onFocus}
name="seconds"
no-spinner no-spinner
.required=${this.required} .required=${this.required}
.autoValidate=${this.autoValidate} .autoValidate=${this.autoValidate}
maxlength="2"
max="59"
min="0" min="0"
.disabled=${this.disabled} .disabled=${this.disabled}
.suffix=${this.enableMillisecond ? ":" : ""} suffix=":"
class=${this.enableMillisecond ? "has-suffix" : ""} class="hasSuffix"
> >
</ha-textfield>` </ha-textfield>
: ""} `
${this.enableMillisecond : ""}
? html`<ha-textfield
id="millisec"
type="number"
.value=${this._formatValue(this.milliseconds, 3)}
.label=${this.millisecLabel}
@change=${this._valueChanged}
@focusin=${this._onFocus}
name="milliseconds"
no-spinner
.required=${this.required}
.autoValidate=${this.autoValidate}
maxlength="3"
max="999"
min="0"
.disabled=${this.disabled}
>
</ha-textfield>`
: ""}
${this.clearable && !this.required && !this.disabled
? html`<ha-icon-button
label="clear"
@click=${this._clearValue}
.path=${mdiClose}
></ha-icon-button>`
: nothing}
</div>
<ha-textfield
id="hour"
type="number"
inputmode="numeric"
.value=${this.hours.toFixed()}
.label=${this.hourLabel}
name="hours"
@change=${this._valueChanged}
@focusin=${this._onFocus}
no-spinner
.required=${this.required}
.autoValidate=${this.autoValidate}
maxlength="2"
max=${ifDefined(this._hourMax)}
min="0"
.disabled=${this.disabled}
suffix=":"
class="hasSuffix"
>
</ha-textfield>
<ha-textfield
id="min"
type="number"
inputmode="numeric"
.value=${this._formatValue(this.minutes)}
.label=${this.minLabel}
@change=${this._valueChanged}
@focusin=${this._onFocus}
name="minutes"
no-spinner
.required=${this.required}
.autoValidate=${this.autoValidate}
maxlength="2"
max="59"
min="0"
.disabled=${this.disabled}
.suffix=${this.enableSecond ? ":" : ""}
class=${this.enableSecond ? "has-suffix" : ""}
>
</ha-textfield>
${this.enableSecond
? html`<ha-textfield
id="sec"
type="number"
inputmode="numeric"
.value=${this._formatValue(this.seconds)}
.label=${this.secLabel}
@change=${this._valueChanged}
@focusin=${this._onFocus}
name="seconds"
no-spinner
.required=${this.required}
.autoValidate=${this.autoValidate}
maxlength="2"
max="59"
min="0"
.disabled=${this.disabled}
.suffix=${this.enableMillisecond ? ":" : ""}
class=${this.enableMillisecond ? "has-suffix" : ""}
>
</ha-textfield>`
: ""}
${this.enableMillisecond
? html`<ha-textfield
id="millisec"
type="number"
.value=${this._formatValue(this.milliseconds, 3)}
.label=${this.millisecLabel}
@change=${this._valueChanged}
@focusin=${this._onFocus}
name="milliseconds"
no-spinner
.required=${this.required}
.autoValidate=${this.autoValidate}
maxlength="3"
max="999"
min="0"
.disabled=${this.disabled}
>
</ha-textfield>`
: ""}
${this.format === 24 ${this.format === 24
? "" ? ""
: html`<ha-select : html`<ha-select
@@ -263,17 +249,13 @@ export class HaBaseTimeInput extends LitElement {
<mwc-list-item value="AM">AM</mwc-list-item> <mwc-list-item value="AM">AM</mwc-list-item>
<mwc-list-item value="PM">PM</mwc-list-item> <mwc-list-item value="PM">PM</mwc-list-item>
</ha-select>`} </ha-select>`}
${this.helper
? html`<ha-input-helper-text>${this.helper}</ha-input-helper-text>`
: ""}
</div> </div>
${this.helper
? html`<ha-input-helper-text>${this.helper}</ha-input-helper-text>`
: ""}
`; `;
} }
private _clearValue(): void {
fireEvent(this, "value-changed");
}
private _valueChanged(ev: InputEvent) { private _valueChanged(ev: InputEvent) {
const textField = ev.currentTarget as HaTextField; const textField = ev.currentTarget as HaTextField;
this[textField.name] = this[textField.name] =
@@ -320,25 +302,18 @@ export class HaBaseTimeInput extends LitElement {
} }
static styles = css` static styles = css`
:host([clearable]) {
position: relative;
}
:host { :host {
display: block; display: block;
} }
.time-input-wrap-wrap {
display: flex;
}
.time-input-wrap { .time-input-wrap {
display: flex; display: flex;
border-radius: var(--mdc-shape-small, 4px) var(--mdc-shape-small, 4px) 0 0; border-radius: var(--mdc-shape-small, 4px) var(--mdc-shape-small, 4px) 0 0;
overflow: hidden; overflow: hidden;
position: relative; position: relative;
direction: ltr; direction: ltr;
padding-right: 3px;
} }
ha-textfield { ha-textfield {
width: 55px; width: 40px;
text-align: center; text-align: center;
--mdc-shape-small: 0; --mdc-shape-small: 0;
--text-field-appearance: none; --text-field-appearance: none;
@@ -360,21 +335,6 @@ export class HaBaseTimeInput extends LitElement {
--mdc-shape-small: 0; --mdc-shape-small: 0;
width: 85px; width: 85px;
} }
:host([clearable]) .mdc-select__anchor {
padding-inline-end: var(--select-selected-text-padding-end, 12px);
}
ha-icon-button {
position: relative
--mdc-icon-button-size: 36px;
--mdc-icon-size: 20px;
color: var(--secondary-text-color);
direction: var(--direction);
display: flex;
align-items: center;
background-color:var(--mdc-text-field-fill-color, whitesmoke);
border-bottom-style: solid;
border-bottom-width: 1px;
}
label { label {
-moz-osx-font-smoothing: grayscale; -moz-osx-font-smoothing: grayscale;
-webkit-font-smoothing: antialiased; -webkit-font-smoothing: antialiased;

View File

@@ -196,8 +196,8 @@ export class HaControlNumberButton extends LitElement {
--control-number-buttons-background-opacity: 0.2; --control-number-buttons-background-opacity: 0.2;
--control-number-buttons-border-radius: 10px; --control-number-buttons-border-radius: 10px;
--mdc-icon-size: 16px; --mdc-icon-size: 16px;
height: var(--feature-height); height: 40px;
width: 100%; width: 200px;
color: var(--primary-text-color); color: var(--primary-text-color);
-webkit-tap-highlight-color: transparent; -webkit-tap-highlight-color: transparent;
font-style: normal; font-style: normal;

View File

@@ -295,8 +295,6 @@ export class HaFloorPicker extends SubscribeMixin(LitElement) {
icon: null, icon: null,
level: null, level: null,
aliases: [], aliases: [],
created_at: 0,
modified_at: 0,
}, },
]; ];
} }
@@ -311,8 +309,6 @@ export class HaFloorPicker extends SubscribeMixin(LitElement) {
icon: "mdi:plus", icon: "mdi:plus",
level: null, level: null,
aliases: [], aliases: [],
created_at: 0,
modified_at: 0,
}, },
]; ];
} }
@@ -395,8 +391,6 @@ export class HaFloorPicker extends SubscribeMixin(LitElement) {
icon: null, icon: null,
level: null, level: null,
aliases: [], aliases: [],
created_at: 0,
modified_at: 0,
}, },
] as FloorRegistryEntry[]; ] as FloorRegistryEntry[];
} else { } else {
@@ -411,8 +405,6 @@ export class HaFloorPicker extends SubscribeMixin(LitElement) {
icon: "mdi:plus", icon: "mdi:plus",
level: null, level: null,
aliases: [], aliases: [],
created_at: 0,
modified_at: 0,
}, },
] as FloorRegistryEntry[]; ] as FloorRegistryEntry[];
} }

View File

@@ -94,8 +94,6 @@ export const computeInitialHaFormData = (
data[field.name] = selector.color_temp?.min_mireds ?? 153; data[field.name] = selector.color_temp?.min_mireds ?? 153;
} else if ( } else if (
"action" in selector || "action" in selector ||
"trigger" in selector ||
"condition" in selector ||
"media" in selector || "media" in selector ||
"target" in selector "target" in selector
) { ) {

View File

@@ -20,7 +20,7 @@ export class HaGridSizeEditor extends LitElement {
@property({ attribute: false }) public value?: GridSizeValue; @property({ attribute: false }) public value?: GridSizeValue;
@property({ attribute: false }) public rows = 8; @property({ attribute: false }) public rows = 6;
@property({ attribute: false }) public columns = 4; @property({ attribute: false }) public columns = 4;
@@ -205,7 +205,7 @@ export class HaGridSizeEditor extends LitElement {
.preview { .preview {
position: relative; position: relative;
grid-area: preview; grid-area: preview;
aspect-ratio: 1 / 1.2; aspect-ratio: 1 / 1;
} }
.preview > div { .preview > div {
position: absolute; position: absolute;

View File

@@ -303,8 +303,6 @@ export class HaLabelPicker extends SubscribeMixin(LitElement) {
icon: null, icon: null,
color: null, color: null,
description: null, description: null,
created_at: 0,
modified_at: 0,
}, },
]; ];
} }
@@ -319,8 +317,6 @@ export class HaLabelPicker extends SubscribeMixin(LitElement) {
icon: "mdi:plus", icon: "mdi:plus",
color: null, color: null,
description: null, description: null,
created_at: 0,
modified_at: 0,
}, },
]; ];
} }

View File

@@ -28,11 +28,6 @@ const LAWN_MOWER_ACTIONS: Partial<
service: "start_mowing", service: "start_mowing",
feature: LawnMowerEntityFeature.START_MOWING, feature: LawnMowerEntityFeature.START_MOWING,
}, },
returning: {
action: "pause",
service: "pause",
feature: LawnMowerEntityFeature.PAUSE,
},
paused: { paused: {
action: "resume_mowing", action: "resume_mowing",
service: "start_mowing", service: "start_mowing",

View File

@@ -6,12 +6,12 @@ import {
mdiSofa, mdiSofa,
} from "@mdi/js"; } from "@mdi/js";
import { import {
CSSResultGroup,
LitElement,
PropertyValues,
css, css,
CSSResultGroup,
html, html,
LitElement,
nothing, nothing,
PropertyValues,
} from "lit"; } from "lit";
import { customElement, property, state } from "lit/decorators"; import { customElement, property, state } from "lit/decorators";
import { styleMap } from "lit/directives/style-map"; import { styleMap } from "lit/directives/style-map";
@@ -20,7 +20,7 @@ import { fireEvent } from "../common/dom/fire_event";
import { caseInsensitiveStringCompare } from "../common/string/compare"; import { caseInsensitiveStringCompare } from "../common/string/compare";
import { Blueprints, fetchBlueprints } from "../data/blueprint"; import { Blueprints, fetchBlueprints } from "../data/blueprint";
import { ConfigEntry, getConfigEntries } from "../data/config_entries"; import { ConfigEntry, getConfigEntries } from "../data/config_entries";
import { ItemType, RelatedResult, findRelated } from "../data/search"; import { findRelated, ItemType, RelatedResult } from "../data/search";
import { haStyle } from "../resources/styles"; import { haStyle } from "../resources/styles";
import { HomeAssistant } from "../types"; import { HomeAssistant } from "../types";
import { brandsUrl } from "../util/brands-url"; import { brandsUrl } from "../util/brands-url";
@@ -109,26 +109,6 @@ export class HaRelatedItems extends LitElement {
) )
); );
private _getConfigEntries = memoizeOne(
(
relatedConfigEntries: string[] | undefined,
entries: ConfigEntry[] | undefined
) => {
const configEntries =
relatedConfigEntries && entries
? relatedConfigEntries.map((entryId) =>
entries!.find((configEntry) => configEntry.entry_id === entryId)
)
: undefined;
const configEntryDomains = new Set(
configEntries?.map((entry) => entry?.domain)
);
return { configEntries, configEntryDomains };
}
);
protected render() { protected render() {
if (!this._related) { if (!this._related) {
return nothing; return nothing;
@@ -148,25 +128,22 @@ export class HaRelatedItems extends LitElement {
</mwc-list> </mwc-list>
`; `;
} }
const { configEntries, configEntryDomains } = this._getConfigEntries(
this._related.config_entry,
this._entries
);
return html` return html`
${configEntries || this._related.integration ${this._related.config_entry && this._entries
? html`<h3> ? html`<h3>
${this.hass.localize("ui.components.related-items.integration")} ${this.hass.localize("ui.components.related-items.integration")}
</h3> </h3>
<mwc-list <mwc-list
>${configEntries?.map((entry) => { >${this._related.config_entry.map((relatedConfigEntryId) => {
const entry: ConfigEntry | undefined = this._entries!.find(
(configEntry) => configEntry.entry_id === relatedConfigEntryId
);
if (!entry) { if (!entry) {
return nothing; return nothing;
} }
return html` return html`
<a <a
href=${`/config/integrations/integration/${entry.domain}#config_entry=${entry.entry_id}`} href=${`/config/integrations/integration/${entry.domain}#config_entry=${relatedConfigEntryId}`}
@click=${this._navigateAwayClose} @click=${this._navigateAwayClose}
> >
<ha-list-item hasMeta graphic="icon"> <ha-list-item hasMeta graphic="icon">
@@ -187,34 +164,8 @@ export class HaRelatedItems extends LitElement {
</ha-list-item> </ha-list-item>
</a> </a>
`; `;
})} })}</mwc-list
${this._related.integration >`
?.filter((integration) => !configEntryDomains.has(integration))
.map(
(integration) =>
html`<a
href=${`/config/integrations/integration/${integration}`}
@click=${this._navigateAwayClose}
>
<ha-list-item hasMeta graphic="icon">
<img
.src=${brandsUrl({
domain: integration,
type: "icon",
useFallback: true,
darkOptimized: this.hass.themes?.darkMode,
})}
crossorigin="anonymous"
referrerpolicy="no-referrer"
alt=${integration}
slot="graphic"
/>
${this.hass.localize(`component.${integration}.title`)}
<ha-icon-next slot="meta"></ha-icon-next>
</ha-list-item>
</a>`
)}
</mwc-list>`
: nothing} : nothing}
${this._related.device ${this._related.device
? html`<h3> ? html`<h3>

View File

@@ -35,6 +35,10 @@ export class HaActionSelector extends LitElement {
display: block; display: block;
margin-bottom: 16px; margin-bottom: 16px;
} }
:host([disabled]) ha-automation-action {
opacity: var(--light-disabled-opacity);
pointer-events: none;
}
label { label {
display: block; display: block;
margin-bottom: 4px; margin-bottom: 4px;

View File

@@ -1,23 +1,17 @@
import { css, CSSResultGroup, html, LitElement } from "lit"; import { css, CSSResultGroup, html, LitElement } from "lit";
import { customElement, property } from "lit/decorators"; import { customElement, property } from "lit/decorators";
import { fireEvent } from "../../common/dom/fire_event"; import { fireEvent } from "../../common/dom/fire_event";
import { BooleanSelector } from "../../data/selector";
import { HomeAssistant } from "../../types"; import { HomeAssistant } from "../../types";
import "../ha-checkbox";
import "../ha-formfield"; import "../ha-formfield";
import "../ha-input-helper-text";
import "../ha-switch"; import "../ha-switch";
import "../ha-input-helper-text";
@customElement("ha-selector-boolean") @customElement("ha-selector-boolean")
export class HaBooleanSelector extends LitElement { export class HaBooleanSelector extends LitElement {
@property({ attribute: false }) public hass!: HomeAssistant; @property({ attribute: false }) public hass!: HomeAssistant;
@property({ attribute: false }) public selector!: BooleanSelector;
@property({ type: Boolean }) public value = false; @property({ type: Boolean }) public value = false;
@property() public placeholder?: any;
@property() public label?: string; @property() public label?: string;
@property() public helper?: string; @property() public helper?: string;
@@ -25,24 +19,13 @@ export class HaBooleanSelector extends LitElement {
@property({ type: Boolean }) public disabled = false; @property({ type: Boolean }) public disabled = false;
protected render() { protected render() {
const checkbox = this.selector.boolean?.mode === "checkbox";
return html` return html`
<ha-formfield .alignEnd=${!checkbox} spaceBetween .label=${this.label}> <ha-formfield alignEnd spaceBetween .label=${this.label}>
${checkbox <ha-switch
? html` .checked=${this.value}
<ha-checkbox @change=${this._handleChange}
.checked=${this.value ?? this.placeholder === true} .disabled=${this.disabled}
@change=${this._handleChange} ></ha-switch>
.disabled=${this.disabled}
></ha-checkbox>
`
: html`
<ha-switch
.checked=${this.value ?? this.placeholder === true}
@change=${this._handleChange}
.disabled=${this.disabled}
></ha-switch>
`}
</ha-formfield> </ha-formfield>
${this.helper ${this.helper
? html`<ha-input-helper-text>${this.helper}</ha-input-helper-text>` ? html`<ha-input-helper-text>${this.helper}</ha-input-helper-text>`

View File

@@ -35,6 +35,10 @@ export class HaConditionSelector extends LitElement {
display: block; display: block;
margin-bottom: 16px; margin-bottom: 16px;
} }
:host([disabled]) ha-automation-condition {
opacity: var(--light-disabled-opacity);
pointer-events: none;
}
label { label {
display: block; display: block;
margin-bottom: 4px; margin-bottom: 4px;

View File

@@ -30,7 +30,6 @@ export class HaTimeDuration extends LitElement {
.disabled=${this.disabled} .disabled=${this.disabled}
.required=${this.required} .required=${this.required}
?enableDay=${this.selector.duration?.enable_day} ?enableDay=${this.selector.duration?.enable_day}
?enableMillisecond=${this.selector.duration?.enable_millisecond}
></ha-duration-input> ></ha-duration-input>
`; `;
} }

View File

@@ -45,14 +45,7 @@ export class HaImageSelector extends LitElement {
return html` return html`
<div> <div>
<label> <label>
${this.hass.localize( ${this.hass.localize("ui.components.selectors.image.select_image")}
"ui.components.selectors.image.select_image_with_label",
{
label:
this.label ||
this.hass.localize("ui.components.selectors.image.image"),
}
)}
<ha-formfield <ha-formfield
.label=${this.hass.localize("ui.components.selectors.image.upload")} .label=${this.hass.localize("ui.components.selectors.image.upload")}
> >

View File

@@ -57,10 +57,6 @@ const SELECTOR_SCHEMAS = {
name: "enable_day", name: "enable_day",
selector: { boolean: {} }, selector: { boolean: {} },
}, },
{
name: "enable_millisecond",
selector: { boolean: {} },
},
] as const, ] as const,
entity: [ entity: [
{ {

View File

@@ -81,16 +81,15 @@ export class HaTargetSelector extends LitElement {
return nothing; return nothing;
} }
return html` ${this.label ? html`<label>${this.label}</label>` : nothing} return html`<ha-target-picker
<ha-target-picker .hass=${this.hass}
.hass=${this.hass} .value=${this.value}
.value=${this.value} .helper=${this.helper}
.helper=${this.helper} .deviceFilter=${this._filterDevices}
.deviceFilter=${this._filterDevices} .entityFilter=${this._filterEntities}
.entityFilter=${this._filterEntities} .disabled=${this.disabled}
.disabled=${this.disabled} .createDomains=${this._createDomains}
.createDomains=${this._createDomains} ></ha-target-picker>`;
></ha-target-picker>`;
} }
private _filterEntities = (entity: HassEntity): boolean => { private _filterEntities = (entity: HassEntity): boolean => {

View File

@@ -1,18 +1,9 @@
import { css, html, nothing, LitElement } from "lit"; import { css, html, LitElement } from "lit";
import { customElement, property, state } from "lit/decorators"; import { customElement, property } from "lit/decorators";
import { fireEvent } from "../../common/dom/fire_event"; import { fireEvent } from "../../common/dom/fire_event";
import { HomeAssistant } from "../../types"; import { HomeAssistant } from "../../types";
import { documentationUrl } from "../../util/documentation-url";
import "../ha-code-editor"; import "../ha-code-editor";
import "../ha-input-helper-text"; import "../ha-input-helper-text";
import "../ha-alert";
const WARNING_STRINGS = [
"template:",
"sensor:",
"state:",
"platform: template",
];
@customElement("ha-selector-template") @customElement("ha-selector-template")
export class HaTemplateSelector extends LitElement { export class HaTemplateSelector extends LitElement {
@@ -28,33 +19,9 @@ export class HaTemplateSelector extends LitElement {
@property({ type: Boolean }) public required = true; @property({ type: Boolean }) public required = true;
@state() private warn: string | undefined = undefined;
protected render() { protected render() {
return html` return html`
${this.warn ${this.label ? html`<p>${this.label}${this.required ? "*" : ""}</p>` : ""}
? html`<ha-alert alert-type="warning"
>${this.hass.localize(
"ui.components.selectors.template.yaml_warning",
{ string: this.warn }
)}
<br />
<a
target="_blank"
rel="noopener noreferrer"
href=${documentationUrl(
this.hass,
"/docs/configuration/templating/"
)}
>${this.hass.localize(
"ui.components.selectors.template.learn_more"
)}</a
></ha-alert
>`
: nothing}
${this.label
? html`<p>${this.label}${this.required ? "*" : ""}</p>`
: nothing}
<ha-code-editor <ha-code-editor
mode="jinja2" mode="jinja2"
.hass=${this.hass} .hass=${this.hass}
@@ -69,7 +36,7 @@ export class HaTemplateSelector extends LitElement {
></ha-code-editor> ></ha-code-editor>
${this.helper ${this.helper
? html`<ha-input-helper-text>${this.helper}</ha-input-helper-text>` ? html`<ha-input-helper-text>${this.helper}</ha-input-helper-text>`
: nothing} : ""}
`; `;
} }
@@ -78,7 +45,6 @@ export class HaTemplateSelector extends LitElement {
if (this.value === value) { if (this.value === value) {
return; return;
} }
this.warn = WARNING_STRINGS.find((str) => value.includes(str));
fireEvent(this, "value-changed", { value }); fireEvent(this, "value-changed", { value });
} }

View File

@@ -27,7 +27,6 @@ export class HaTimeSelector extends LitElement {
.locale=${this.hass.locale} .locale=${this.hass.locale}
.disabled=${this.disabled} .disabled=${this.disabled}
.required=${this.required} .required=${this.required}
clearable
.helper=${this.helper} .helper=${this.helper}
.label=${this.label} .label=${this.label}
enable-second enable-second

View File

@@ -35,6 +35,10 @@ export class HaTriggerSelector extends LitElement {
display: block; display: block;
margin-bottom: 16px; margin-bottom: 16px;
} }
:host([disabled]) ha-automation-trigger {
opacity: var(--light-disabled-opacity);
pointer-events: none;
}
label { label {
display: block; display: block;
margin-bottom: 4px; margin-bottom: 4px;

View File

@@ -1,49 +0,0 @@
import { html, LitElement } from "lit";
import { customElement, property } from "lit/decorators";
import { UiStateContentSelector } from "../../data/selector";
import { SubscribeMixin } from "../../mixins/subscribe-mixin";
import { HomeAssistant } from "../../types";
import "../entity/ha-entity-state-content-picker";
@customElement("ha-selector-ui_state_content")
export class HaSelectorUiStateContent extends SubscribeMixin(LitElement) {
@property({ attribute: false }) public hass!: HomeAssistant;
@property({ attribute: false }) public selector!: UiStateContentSelector;
@property() public value?: string | string[];
@property() public label?: string;
@property() public helper?: string;
@property({ type: Boolean }) public disabled = false;
@property({ type: Boolean }) public required = true;
@property({ attribute: false }) public context?: {
filter_entity?: string;
};
protected render() {
return html`
<ha-entity-state-content-picker
.hass=${this.hass}
.entityId=${this.selector.ui_state_content?.entity_id ||
this.context?.filter_entity}
.value=${this.value}
.label=${this.label}
.helper=${this.helper}
.disabled=${this.disabled}
.required=${this.required}
.allowName=${this.selector.ui_state_content?.allow_name}
></ha-entity-state-content-picker>
`;
}
}
declare global {
interface HTMLElementTagNameMap {
"ha-selector-ui_state_content": HaSelectorUiStateContent;
}
}

View File

@@ -57,7 +57,6 @@ const LOAD_ELEMENTS = {
color_temp: () => import("./ha-selector-color-temp"), color_temp: () => import("./ha-selector-color-temp"),
ui_action: () => import("./ha-selector-ui-action"), ui_action: () => import("./ha-selector-ui-action"),
ui_color: () => import("./ha-selector-ui-color"), ui_color: () => import("./ha-selector-ui-color"),
ui_state_content: () => import("./ha-selector-ui-state-content"),
}; };
const LEGACY_UI_SELECTORS = new Set(["ui-action", "ui-color"]); const LEGACY_UI_SELECTORS = new Set(["ui-action", "ui-color"]);

View File

@@ -77,7 +77,7 @@ export class HaServiceControl extends LitElement {
@property({ attribute: false }) public hass!: HomeAssistant; @property({ attribute: false }) public hass!: HomeAssistant;
@property({ attribute: false }) public value?: { @property({ attribute: false }) public value?: {
action: string; service: string;
target?: HassServiceTarget; target?: HassServiceTarget;
data?: Record<string, any>; data?: Record<string, any>;
}; };
@@ -112,23 +112,23 @@ export class HaServiceControl extends LitElement {
| undefined | undefined
| this["value"]; | this["value"];
if (oldValue?.action !== this.value?.action) { if (oldValue?.service !== this.value?.service) {
this._checkedKeys = new Set(); this._checkedKeys = new Set();
} }
const serviceData = this._getServiceInfo( const serviceData = this._getServiceInfo(
this.value?.action, this.value?.service,
this.hass.services this.hass.services
); );
// Fetch the manifest if we have a service selected and the service domain changed. // Fetch the manifest if we have a service selected and the service domain changed.
// If no service is selected, clear the manifest. // If no service is selected, clear the manifest.
if (this.value?.action) { if (this.value?.service) {
if ( if (
!oldValue?.action || !oldValue?.service ||
computeDomain(this.value.action) !== computeDomain(oldValue.action) computeDomain(this.value.service) !== computeDomain(oldValue.service)
) { ) {
this._fetchManifest(computeDomain(this.value?.action)); this._fetchManifest(computeDomain(this.value?.service));
} }
} else { } else {
this._manifest = undefined; this._manifest = undefined;
@@ -168,7 +168,7 @@ export class HaServiceControl extends LitElement {
this._value = this.value; this._value = this.value;
} }
if (oldValue?.action !== this.value?.action) { if (oldValue?.service !== this.value?.service) {
let updatedDefaultValue = false; let updatedDefaultValue = false;
if (this._value && serviceData) { if (this._value && serviceData) {
const loadDefaults = this.value && !("data" in this.value); const loadDefaults = this.value && !("data" in this.value);
@@ -367,7 +367,7 @@ export class HaServiceControl extends LitElement {
protected render() { protected render() {
const serviceData = this._getServiceInfo( const serviceData = this._getServiceInfo(
this._value?.action, this._value?.service,
this.hass.services this.hass.services
); );
@@ -392,11 +392,11 @@ export class HaServiceControl extends LitElement {
this._value this._value
); );
const domain = this._value?.action const domain = this._value?.service
? computeDomain(this._value.action) ? computeDomain(this._value.service)
: undefined; : undefined;
const serviceName = this._value?.action const serviceName = this._value?.service
? computeObjectId(this._value.action) ? computeObjectId(this._value.service)
: undefined; : undefined;
const description = const description =
@@ -410,7 +410,7 @@ export class HaServiceControl extends LitElement {
? nothing ? nothing
: html`<ha-service-picker : html`<ha-service-picker
.hass=${this.hass} .hass=${this.hass}
.value=${this._value?.action} .value=${this._value?.service}
.disabled=${this.disabled} .disabled=${this.disabled}
@value-changed=${this._serviceChanged} @value-changed=${this._serviceChanged}
></ha-service-picker>`} ></ha-service-picker>`}
@@ -451,7 +451,7 @@ export class HaServiceControl extends LitElement {
> >
<span slot="description" <span slot="description"
>${this.hass.localize( >${this.hass.localize(
"ui.components.service-control.target_secondary" "ui.components.service-control.target_description"
)}</span )}</span
><ha-selector ><ha-selector
.hass=${this.hass} .hass=${this.hass}
@@ -478,9 +478,7 @@ export class HaServiceControl extends LitElement {
${shouldRenderServiceDataYaml ${shouldRenderServiceDataYaml
? html`<ha-yaml-editor ? html`<ha-yaml-editor
.hass=${this.hass} .hass=${this.hass}
.label=${this.hass.localize( .label=${this.hass.localize("ui.components.service-control.data")}
"ui.components.service-control.action_data"
)}
.name=${"data"} .name=${"data"}
.readOnly=${this.disabled} .readOnly=${this.disabled}
.defaultValue=${this._value?.data} .defaultValue=${this._value?.data}
@@ -497,11 +495,6 @@ export class HaServiceControl extends LitElement {
dataField.name || dataField.name ||
dataField.key} dataField.key}
> >
${this._renderSectionDescription(
dataField,
domain,
serviceName
)}
${Object.entries(dataField.fields).map(([key, field]) => ${Object.entries(dataField.fields).map(([key, field]) =>
this._renderField( this._renderField(
{ key, ...field }, { key, ...field },
@@ -522,22 +515,6 @@ export class HaServiceControl extends LitElement {
)} `; )} `;
} }
private _renderSectionDescription(
dataField: ExtHassService["fields"][number],
domain: string | undefined,
serviceName: string | undefined
) {
const description = this.hass!.localize(
`component.${domain}.services.${serviceName}.sections.${dataField.key}.description`
);
if (!description) {
return nothing;
}
return html`<p>${description}</p>`;
}
private _renderField = ( private _renderField = (
dataField: ExtHassService["fields"][number], dataField: ExtHassService["fields"][number],
hasOptional: boolean, hasOptional: boolean,
@@ -617,11 +594,11 @@ export class HaServiceControl extends LitElement {
}; };
private _localizeValueCallback = (key: string) => { private _localizeValueCallback = (key: string) => {
if (!this._value?.action) { if (!this._value?.service) {
return ""; return "";
} }
return this.hass.localize( return this.hass.localize(
`component.${computeDomain(this._value.action)}.selector.${key}` `component.${computeDomain(this._value.service)}.selector.${key}`
); );
}; };
@@ -633,7 +610,7 @@ export class HaServiceControl extends LitElement {
if (checked) { if (checked) {
this._checkedKeys.add(key); this._checkedKeys.add(key);
const field = this._getServiceInfo( const field = this._getServiceInfo(
this._value?.action, this._value?.service,
this.hass.services this.hass.services
)?.fields.find((_field) => _field.key === key); )?.fields.find((_field) => _field.key === key);
@@ -679,7 +656,7 @@ export class HaServiceControl extends LitElement {
private _serviceChanged(ev: ValueChangedEvent<string>) { private _serviceChanged(ev: ValueChangedEvent<string>) {
ev.stopPropagation(); ev.stopPropagation();
if (ev.detail.value === this._value?.action) { if (ev.detail.value === this._value?.service) {
return; return;
} }
@@ -738,7 +715,7 @@ export class HaServiceControl extends LitElement {
} }
const value = { const value = {
action: newService, service: newService,
target, target,
}; };

View File

@@ -46,7 +46,7 @@ class HaServicePicker extends LitElement {
return html` return html`
<ha-combo-box <ha-combo-box
.hass=${this.hass} .hass=${this.hass}
.label=${this.hass.localize("ui.components.service-picker.action")} .label=${this.hass.localize("ui.components.service-picker.service")}
.filteredItems=${this._filteredServices( .filteredItems=${this._filteredServices(
this.hass.localize, this.hass.localize,
this.hass.services, this.hass.services,

View File

@@ -210,8 +210,6 @@ class HaSidebar extends SubscribeMixin(LitElement) {
private _editStyleLoaded = false; private _editStyleLoaded = false;
private _unsubPersistentNotifications: UnsubscribeFunc | undefined;
@storage({ @storage({
key: "sidebarPanelOrder", key: "sidebarPanelOrder",
state: true, state: true,
@@ -285,26 +283,15 @@ class HaSidebar extends SubscribeMixin(LitElement) {
hass.localize !== oldHass.localize || hass.localize !== oldHass.localize ||
hass.locale !== oldHass.locale || hass.locale !== oldHass.locale ||
hass.states !== oldHass.states || hass.states !== oldHass.states ||
hass.defaultPanel !== oldHass.defaultPanel || hass.defaultPanel !== oldHass.defaultPanel
hass.connected !== oldHass.connected
); );
} }
protected firstUpdated(changedProps: PropertyValues) { protected firstUpdated(changedProps: PropertyValues) {
super.firstUpdated(changedProps); super.firstUpdated(changedProps);
this.subscribePersistentNotifications(); subscribeNotifications(this.hass.connection, (notifications) => {
} this._notifications = notifications;
});
private subscribePersistentNotifications(): void {
if (this._unsubPersistentNotifications) {
this._unsubPersistentNotifications();
}
this._unsubPersistentNotifications = subscribeNotifications(
this.hass.connection,
(notifications) => {
this._notifications = notifications;
}
);
} }
protected updated(changedProps) { protected updated(changedProps) {
@@ -319,14 +306,6 @@ class HaSidebar extends SubscribeMixin(LitElement) {
return; return;
} }
if (
this.hass &&
changedProps.get("hass")?.connected === false &&
this.hass.connected === true
) {
this.subscribePersistentNotifications();
}
this._calculateCounts(); this._calculateCounts();
if (!SUPPORT_SCROLL_IF_NEEDED) { if (!SUPPORT_SCROLL_IF_NEEDED) {

View File

@@ -15,7 +15,6 @@ export class HaSlider extends MdSlider {
css` css`
:host { :host {
--md-sys-color-primary: var(--primary-color); --md-sys-color-primary: var(--primary-color);
--md-sys-color-on-primary: var(--text-primary-color);
--md-sys-color-outline: var(--outline-color); --md-sys-color-outline: var(--outline-color);
--md-sys-color-on-surface: var(--primary-text-color); --md-sys-color-on-surface: var(--primary-text-color);
--md-slider-handle-width: 14px; --md-slider-handle-width: 14px;

View File

@@ -23,8 +23,6 @@ export class HaTimeInput extends LitElement {
@property({ type: Boolean, attribute: "enable-second" }) @property({ type: Boolean, attribute: "enable-second" })
public enableSecond = false; public enableSecond = false;
@property({ type: Boolean, reflect: true }) public clearable?: boolean;
protected render() { protected render() {
const useAMPM = useAmPm(this.locale); const useAMPM = useAmPm(this.locale);
@@ -50,26 +48,22 @@ export class HaTimeInput extends LitElement {
@value-changed=${this._timeChanged} @value-changed=${this._timeChanged}
.enableSecond=${this.enableSecond} .enableSecond=${this.enableSecond}
.required=${this.required} .required=${this.required}
.clearable=${this.clearable && this.value !== undefined}
.helper=${this.helper} .helper=${this.helper}
></ha-base-time-input> ></ha-base-time-input>
`; `;
} }
private _timeChanged(ev: CustomEvent<{ value?: TimeChangedEvent }>) { private _timeChanged(ev: CustomEvent<{ value: TimeChangedEvent }>) {
ev.stopPropagation(); ev.stopPropagation();
const eventValue = ev.detail.value; const eventValue = ev.detail.value;
const useAMPM = useAmPm(this.locale); const useAMPM = useAmPm(this.locale);
let value: string | undefined; let value;
// An undefined eventValue means the time selector is being cleared,
// the `value` variable will (intentionally) be left undefined.
if ( if (
eventValue !== undefined && !isNaN(eventValue.hours) ||
(!isNaN(eventValue.hours) || !isNaN(eventValue.minutes) ||
!isNaN(eventValue.minutes) || !isNaN(eventValue.seconds)
!isNaN(eventValue.seconds))
) { ) {
let hours = eventValue.hours || 0; let hours = eventValue.hours || 0;
if (eventValue && useAMPM) { if (eventValue && useAMPM) {

View File

@@ -49,8 +49,6 @@ export class HaYamlEditor extends LitElement {
@property({ type: Boolean }) public copyClipboard = false; @property({ type: Boolean }) public copyClipboard = false;
@property({ type: Boolean }) public hasExtraActions = false;
@state() private _yaml = ""; @state() private _yaml = "";
public setValue(value): void { public setValue(value): void {
@@ -102,16 +100,13 @@ export class HaYamlEditor extends LitElement {
@value-changed=${this._onChange} @value-changed=${this._onChange}
dir="ltr" dir="ltr"
></ha-code-editor> ></ha-code-editor>
${this.copyClipboard || this.hasExtraActions ${this.copyClipboard
? html`<div class="card-actions"> ? html`<div class="card-actions">
${this.copyClipboard <mwc-button @click=${this._copyYaml}>
? html` <mwc-button @click=${this._copyYaml}> ${this.hass.localize(
${this.hass.localize( "ui.components.yaml-editor.copy_to_clipboard"
"ui.components.yaml-editor.copy_to_clipboard" )}
)} </mwc-button>
</mwc-button>`
: nothing}
<slot name="extra-actions"></slot>
</div>` </div>`
: nothing} : nothing}
`; `;

View File

@@ -483,12 +483,12 @@ export class HaMap extends ReactiveElement {
const entityName = const entityName =
typeof entity !== "string" && entity.label_mode === "state" typeof entity !== "string" && entity.label_mode === "state"
? this.hass.formatEntityState(stateObj) ? this.hass.formatEntityState(stateObj)
: (customTitle ?? : customTitle ??
title title
.split(" ") .split(" ")
.map((part) => part[0]) .map((part) => part[0])
.join("") .join("")
.substr(0, 3)); .substr(0, 3);
// create marker with the icon // create marker with the icon
const marker = Leaflet.marker([latitude, longitude], { const marker = Leaflet.marker([latitude, longitude], {

View File

@@ -79,7 +79,7 @@ class SearchInputOutlined extends LitElement {
} }
private async _filterInputChanged(e) { private async _filterInputChanged(e) {
this._filterChanged(e.target.value); this._filterChanged(e.target.value?.trim());
} }
private async _clearSearch() { private async _clearSearch() {

View File

@@ -67,7 +67,7 @@ class SearchInput extends LitElement {
} }
private async _filterInputChanged(e) { private async _filterInputChanged(e) {
this._filterChanged(e.target.value); this._filterChanged(e.target.value?.trim());
} }
private async _clearSearch() { private async _clearSearch() {

View File

@@ -17,7 +17,7 @@ export class HaTileIcon extends LitElement {
return css` return css`
:host { :host {
--tile-icon-color: var(--disabled-color); --tile-icon-color: var(--disabled-color);
--mdc-icon-size: 22px; --mdc-icon-size: 24px;
} }
.shape::before { .shape::before {
content: ""; content: "";
@@ -32,9 +32,9 @@ export class HaTileIcon extends LitElement {
} }
.shape { .shape {
position: relative; position: relative;
width: 36px; width: 40px;
height: 36px; height: 40px;
border-radius: 18px; border-radius: 20px;
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;

View File

@@ -25,9 +25,9 @@ export class HaTileImage extends LitElement {
return css` return css`
.image { .image {
position: relative; position: relative;
width: 36px; width: 40px;
height: 36px; height: 40px;
border-radius: 18px; border-radius: 20px;
display: flex; display: flex;
flex: none; flex: none;
align-items: center; align-items: center;

View File

@@ -33,7 +33,7 @@ export class HaTileInfo extends LitElement {
flex-direction: column; flex-direction: column;
align-items: flex-start; align-items: flex-start;
justify-content: center; justify-content: center;
height: 36px; min-height: 40px;
} }
span { span {
text-overflow: ellipsis; text-overflow: ellipsis;

View File

@@ -424,7 +424,7 @@ export class HatScriptGraph extends LitElement {
return html` return html`
<hat-graph-node <hat-graph-node
.graphStart=${graphStart} .graphStart=${graphStart}
.iconPath=${node.action ? undefined : mdiRoomService} .iconPath=${node.service ? undefined : mdiRoomService}
@focus=${this.selectNode(node, path)} @focus=${this.selectNode(node, path)}
?track=${path in this.trace.trace} ?track=${path in this.trace.trace}
?active=${this.selected === path} ?active=${this.selected === path}
@@ -432,11 +432,11 @@ export class HatScriptGraph extends LitElement {
.error=${this.trace.trace[path]?.some((tr) => tr.error)} .error=${this.trace.trace[path]?.some((tr) => tr.error)}
tabindex=${this.trace && path in this.trace.trace ? "0" : "-1"} tabindex=${this.trace && path in this.trace.trace ? "0" : "-1"}
> >
${node.action ${node.service
? html`<ha-service-icon ? html`<ha-service-icon
slot="icon" slot="icon"
.hass=${this.hass} .hass=${this.hass}
.service=${node.action} .service=${node.service}
></ha-service-icon>` ></ha-service-icon>`
: nothing} : nothing}
</hat-graph-node> </hat-graph-node>

View File

@@ -2,11 +2,10 @@ import { stringCompare } from "../common/string/compare";
import { HomeAssistant } from "../types"; import { HomeAssistant } from "../types";
import { DeviceRegistryEntry } from "./device_registry"; import { DeviceRegistryEntry } from "./device_registry";
import { EntityRegistryEntry } from "./entity_registry"; import { EntityRegistryEntry } from "./entity_registry";
import { RegistryEntry } from "./registry";
export { subscribeAreaRegistry } from "./ws-area_registry"; export { subscribeAreaRegistry } from "./ws-area_registry";
export interface AreaRegistryEntry extends RegistryEntry { export interface AreaRegistryEntry {
area_id: string; area_id: string;
floor_id: string | null; floor_id: string | null;
name: string; name: string;

View File

@@ -6,7 +6,7 @@ import { navigate } from "../common/navigate";
import { Context, HomeAssistant } from "../types"; import { Context, HomeAssistant } from "../types";
import { BlueprintInput } from "./blueprint"; import { BlueprintInput } from "./blueprint";
import { DeviceCondition, DeviceTrigger } from "./device_automation"; import { DeviceCondition, DeviceTrigger } from "./device_automation";
import { Action, MODES, migrateAutomationAction } from "./script"; import { Action, MODES } from "./script";
export const AUTOMATION_DEFAULT_MODE: (typeof MODES)[number] = "single"; export const AUTOMATION_DEFAULT_MODE: (typeof MODES)[number] = "single";
export const AUTOMATION_DEFAULT_MAX = 10; export const AUTOMATION_DEFAULT_MAX = 10;
@@ -28,7 +28,7 @@ export interface ManualAutomationConfig {
description?: string; description?: string;
trigger: Trigger | Trigger[]; trigger: Trigger | Trigger[];
condition?: Condition | Condition[]; condition?: Condition | Condition[];
action?: Action | Action[]; action: Action | Action[];
mode?: (typeof MODES)[number]; mode?: (typeof MODES)[number];
max?: number; max?: number;
max_exceeded?: max_exceeded?:
@@ -357,7 +357,7 @@ export const normalizeAutomationConfig = <
>( >(
config: T config: T
): T => { ): T => {
// Normalize data: ensure triggers, actions and conditions are lists // Normalize data: ensure trigger, action and condition are lists
// Happens when people copy paste their automations into the config // Happens when people copy paste their automations into the config
for (const key of ["trigger", "condition", "action"]) { for (const key of ["trigger", "condition", "action"]) {
const value = config[key]; const value = config[key];
@@ -365,11 +365,6 @@ export const normalizeAutomationConfig = <
config[key] = [value]; config[key] = [value];
} }
} }
if (config.action) {
config.action = migrateAutomationAction(config.action);
}
return config; return config;
}; };

View File

@@ -28,14 +28,13 @@ export type HvacMode = (typeof HVAC_MODES)[number];
export const CLIMATE_PRESET_NONE = "none"; export const CLIMATE_PRESET_NONE = "none";
export type HvacAction = export type HvacAction =
| "cooling"
| "defrosting"
| "drying"
| "fan"
| "heating"
| "idle"
| "off" | "off"
| "preheating"; | "preheating"
| "heating"
| "cooling"
| "drying"
| "idle"
| "fan";
export type ClimateEntity = HassEntityBase & { export type ClimateEntity = HassEntityBase & {
attributes: HassEntityAttributeBase & { attributes: HassEntityAttributeBase & {
@@ -90,13 +89,12 @@ export const compareClimateHvacModes = (mode1: HvacMode, mode2: HvacMode) =>
export const CLIMATE_HVAC_ACTION_TO_MODE: Record<HvacAction, HvacMode> = { export const CLIMATE_HVAC_ACTION_TO_MODE: Record<HvacAction, HvacMode> = {
cooling: "cool", cooling: "cool",
defrosting: "heat",
drying: "dry", drying: "dry",
fan: "fan_only", fan: "fan_only",
preheating: "heat",
heating: "heat", heating: "heat",
idle: "off", idle: "off",
off: "off", off: "off",
preheating: "heat",
}; };
export const CLIMATE_HVAC_MODE_ICONS: Record<HvacMode, string> = { export const CLIMATE_HVAC_MODE_ICONS: Record<HvacMode, string> = {

View File

@@ -1,10 +1,6 @@
import { ensureArray } from "../common/array/ensure-array"; import { ensureArray } from "../common/array/ensure-array";
import { HomeAssistant } from "../types"; import { HomeAssistant } from "../types";
export const enum ConversationEntityFeature {
CONTROL = 1,
}
interface IntentTarget { interface IntentTarget {
type: "area" | "device" | "entity" | "domain" | "device_class" | "custom"; type: "area" | "device" | "entity" | "domain" | "device_class" | "custom";
name: string; name: string;

View File

@@ -115,8 +115,8 @@ export function computeCoverPositionStateDisplay(
position?: number position?: number
) { ) {
const statePosition = stateActive(stateObj) const statePosition = stateActive(stateObj)
? (stateObj.attributes.current_position ?? ? stateObj.attributes.current_position ??
stateObj.attributes.current_tilt_position) stateObj.attributes.current_tilt_position
: undefined; : undefined;
const currentPosition = position ?? statePosition; const currentPosition = position ?? statePosition;

View File

@@ -178,11 +178,7 @@ const getEntityName = (
entityId: string | undefined entityId: string | undefined
): string => { ): string => {
if (!entityId) { if (!entityId) {
return ( return "<unknown entity>";
"<" +
hass.localize("ui.panel.config.automation.editor.unknown_entity") +
">"
);
} }
if (entityId.includes(".")) { if (entityId.includes(".")) {
const state = hass.states[entityId]; const state = hass.states[entityId];
@@ -195,11 +191,7 @@ const getEntityName = (
if (entityReg) { if (entityReg) {
return computeEntityRegistryName(hass, entityReg) || entityId; return computeEntityRegistryName(hass, entityReg) || entityId;
} }
return ( return "<unknown entity>";
"<" +
hass.localize("ui.panel.config.automation.editor.unknown_entity") +
">"
);
}; };
export const localizeDeviceAutomationAction = ( export const localizeDeviceAutomationAction = (

View File

@@ -1,27 +1,25 @@
import { computeStateName } from "../common/entity/compute_state_name"; import { computeStateName } from "../common/entity/compute_state_name";
import { caseInsensitiveStringCompare } from "../common/string/compare"; import { caseInsensitiveStringCompare } from "../common/string/compare";
import type { HomeAssistant } from "../types"; import type { HomeAssistant } from "../types";
import { ConfigEntry } from "./config_entries";
import type { import type {
EntityRegistryDisplayEntry, EntityRegistryDisplayEntry,
EntityRegistryEntry, EntityRegistryEntry,
} from "./entity_registry"; } from "./entity_registry";
import { ConfigEntry } from "./config_entries";
import type { EntitySources } from "./entity_sources"; import type { EntitySources } from "./entity_sources";
import { RegistryEntry } from "./registry";
export { export {
fetchDeviceRegistry, fetchDeviceRegistry,
subscribeDeviceRegistry, subscribeDeviceRegistry,
} from "./ws-device_registry"; } from "./ws-device_registry";
export interface DeviceRegistryEntry extends RegistryEntry { export interface DeviceRegistryEntry {
id: string; id: string;
config_entries: string[]; config_entries: string[];
connections: Array<[string, string]>; connections: Array<[string, string]>;
identifiers: Array<[string, string]>; identifiers: Array<[string, string]>;
manufacturer: string | null; manufacturer: string | null;
model: string | null; model: string | null;
model_id: string | null;
name: string | null; name: string | null;
labels: string[]; labels: string[];
sw_version: string | null; sw_version: string | null;

Some files were not shown because too many files have changed in this diff Show More