mirror of
https://github.com/home-assistant/frontend.git
synced 2025-07-29 12:16:39 +00:00
Merge pull request #6156 from home-assistant/dev
This commit is contained in:
commit
ca8586789a
@ -1,4 +0,0 @@
|
||||
node_modules
|
||||
hass_frontend
|
||||
hass_frontend_es5
|
||||
.git
|
31
Dockerfile
31
Dockerfile
@ -1,31 +0,0 @@
|
||||
FROM node:8.11.1-alpine
|
||||
|
||||
# install yarn
|
||||
ENV PATH /root/.yarn/bin:$PATH
|
||||
|
||||
## Install/force base tools
|
||||
RUN apk update \
|
||||
&& apk add make g++ curl bash binutils tar git python2 python3 \
|
||||
&& rm -rf /var/cache/apk/* \
|
||||
&& /bin/bash \
|
||||
&& touch ~/.bashrc
|
||||
|
||||
## Install yarn
|
||||
RUN curl -o- -L https://yarnpkg.com/install.sh | bash
|
||||
|
||||
## Setup the project
|
||||
RUN mkdir -p /frontend
|
||||
|
||||
WORKDIR /frontend
|
||||
|
||||
COPY package.json yarn.lock ./
|
||||
|
||||
RUN yarn install --frozen-lockfile
|
||||
|
||||
COPY . .
|
||||
|
||||
COPY script/docker_entrypoint.sh /usr/bin/docker_entrypoint.sh
|
||||
|
||||
RUN chmod +x /usr/bin/docker_entrypoint.sh
|
||||
|
||||
CMD [ "docker_entrypoint.sh" ]
|
@ -22,15 +22,6 @@ This is the repository for the official [Home Assistant](https://home-assistant.
|
||||
|
||||
A complete guide can be found at the following [link](https://www.home-assistant.io/developers/frontend/). It describes a short guide for the build of project.
|
||||
|
||||
### Docker environment
|
||||
|
||||
It is possible to compile the project and/or run commands in the development environment having only the [Docker](https://www.docker.com) pre-installed in the system. On the root of project you can do:
|
||||
|
||||
- `sh ./script/docker_run.sh build` Build all the project with one command
|
||||
- `sh ./script/docker_run.sh bash` Open an interactive shell (the same environment generated by the _classic environment_) where you can run commands. This bash work on your project directory and any change on your file is automatically present within your build bash.
|
||||
|
||||
**Note**: if you have installed `npm` in addition to the `docker`, you can use the commands `npm run docker_build` and `npm run bash` to get a full build or bash as explained above
|
||||
|
||||
## License
|
||||
|
||||
Home Assistant is open-source and Apache 2 licensed. Feel free to browse the repository, learn and reuse parts in your own projects.
|
||||
|
@ -85,8 +85,8 @@ module.exports.babelExclude = () => [
|
||||
const outputPath = (outputRoot, latestBuild) =>
|
||||
path.resolve(outputRoot, latestBuild ? "frontend_latest" : "frontend_es5");
|
||||
|
||||
const publicPath = (latestBuild) =>
|
||||
latestBuild ? "/frontend_latest/" : "/frontend_es5/";
|
||||
const publicPath = (latestBuild, root = "") =>
|
||||
latestBuild ? `${root}/frontend_latest/` : `${root}/frontend_es5/`;
|
||||
|
||||
/*
|
||||
BundleConfig {
|
||||
@ -170,15 +170,12 @@ module.exports.config = {
|
||||
},
|
||||
|
||||
hassio({ isProdBuild, latestBuild }) {
|
||||
if (latestBuild) {
|
||||
throw new Error("Hass.io does not support latest build!");
|
||||
}
|
||||
return {
|
||||
entry: {
|
||||
entrypoint: path.resolve(paths.hassio_dir, "src/entrypoint.ts"),
|
||||
},
|
||||
outputPath: paths.hassio_output_root,
|
||||
publicPath: paths.hassio_publicPath,
|
||||
outputPath: outputPath(paths.hassio_output_root, latestBuild),
|
||||
publicPath: publicPath(latestBuild, paths.hassio_publicPath),
|
||||
isProdBuild,
|
||||
latestBuild,
|
||||
dontHash: new Set(["entrypoint"]),
|
||||
|
@ -20,6 +20,7 @@ gulp.task(
|
||||
"translations-enable-merge-backend",
|
||||
gulp.parallel("gen-icons-json", "build-translations"),
|
||||
"copy-static-cast",
|
||||
"gen-index-cast-dev",
|
||||
env.useRollup() ? "rollup-dev-server-cast" : "webpack-dev-server-cast"
|
||||
)
|
||||
);
|
||||
|
@ -6,30 +6,32 @@ const merge = require("merge-stream");
|
||||
const path = require("path");
|
||||
const paths = require("../paths");
|
||||
|
||||
const zopfliOptions = { threshold: 150 };
|
||||
|
||||
gulp.task("compress-app", function compressApp() {
|
||||
const jsLatest = gulp
|
||||
.src(path.resolve(paths.app_output_latest, "**/*.js"))
|
||||
.pipe(zopfli({ threshold: 150 }))
|
||||
.pipe(zopfli(zopfliOptions))
|
||||
.pipe(gulp.dest(paths.app_output_latest));
|
||||
|
||||
const jsEs5 = gulp
|
||||
.src(path.resolve(paths.app_output_es5, "**/*.js"))
|
||||
.pipe(zopfli({ threshold: 150 }))
|
||||
.pipe(zopfli(zopfliOptions))
|
||||
.pipe(gulp.dest(paths.app_output_es5));
|
||||
|
||||
const polyfills = gulp
|
||||
.src(path.resolve(paths.app_output_static, "polyfills/*.js"))
|
||||
.pipe(zopfli({ threshold: 150 }))
|
||||
.pipe(zopfli(zopfliOptions))
|
||||
.pipe(gulp.dest(path.resolve(paths.app_output_static, "polyfills")));
|
||||
|
||||
const translations = gulp
|
||||
.src(path.resolve(paths.app_output_static, "translations/**/*.json"))
|
||||
.pipe(zopfli({ threshold: 150 }))
|
||||
.pipe(zopfli(zopfliOptions))
|
||||
.pipe(gulp.dest(path.resolve(paths.app_output_static, "translations")));
|
||||
|
||||
const icons = gulp
|
||||
.src(path.resolve(paths.app_output_static, "mdi/*.json"))
|
||||
.pipe(zopfli({ threshold: 150 }))
|
||||
.pipe(zopfli(zopfliOptions))
|
||||
.pipe(gulp.dest(path.resolve(paths.app_output_static, "mdi")));
|
||||
|
||||
return merge(jsLatest, jsEs5, polyfills, translations, icons);
|
||||
@ -38,6 +40,6 @@ gulp.task("compress-app", function compressApp() {
|
||||
gulp.task("compress-hassio", function compressApp() {
|
||||
return gulp
|
||||
.src(path.resolve(paths.hassio_output_root, "**/*.js"))
|
||||
.pipe(zopfli())
|
||||
.pipe(zopfli(zopfliOptions))
|
||||
.pipe(gulp.dest(paths.hassio_output_root));
|
||||
});
|
||||
|
@ -36,11 +36,13 @@ function copyMdiIcons(staticDir) {
|
||||
function copyPolyfills(staticDir) {
|
||||
const staticPath = genStaticPath(staticDir);
|
||||
|
||||
// Web Component polyfills and adapters
|
||||
// For custom panels using ES5 builds that don't use Babel 7+
|
||||
copyFileDir(
|
||||
npmPath("@webcomponents/webcomponentsjs/custom-elements-es5-adapter.js"),
|
||||
staticPath("polyfills/")
|
||||
);
|
||||
|
||||
// Web Component polyfills and adapters
|
||||
copyFileDir(
|
||||
npmPath("@webcomponents/webcomponentsjs/webcomponents-bundle.js"),
|
||||
staticPath("polyfills/")
|
||||
|
@ -1,6 +1,9 @@
|
||||
const gulp = require("gulp");
|
||||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
|
||||
const env = require("../env");
|
||||
const paths = require("../paths");
|
||||
|
||||
require("./clean.js");
|
||||
require("./gen-icons-json.js");
|
||||
@ -8,6 +11,24 @@ require("./webpack.js");
|
||||
require("./compress.js");
|
||||
require("./rollup.js");
|
||||
|
||||
async function writeEntrypointJS() {
|
||||
// We ship two builds and we need to do feature detection on what version to load.
|
||||
fs.mkdirSync(paths.hassio_output_root, { recursive: true });
|
||||
fs.writeFileSync(
|
||||
path.resolve(paths.hassio_output_root, "entrypoint.js"),
|
||||
`
|
||||
try {
|
||||
new Function("import('${paths.hassio_publicPath}/frontend_latest/entrypoint.js')")();
|
||||
} catch (err) {
|
||||
var el = document.createElement('script');
|
||||
el.src = '${paths.hassio_publicPath}/frontend_es5/entrypoint.js';
|
||||
document.body.appendChild(el);
|
||||
}
|
||||
`,
|
||||
{ encoding: "utf-8" }
|
||||
);
|
||||
}
|
||||
|
||||
gulp.task(
|
||||
"develop-hassio",
|
||||
gulp.series(
|
||||
@ -16,6 +37,7 @@ gulp.task(
|
||||
},
|
||||
"clean-hassio",
|
||||
"gen-icons-json",
|
||||
writeEntrypointJS,
|
||||
env.useRollup() ? "rollup-watch-hassio" : "webpack-watch-hassio"
|
||||
)
|
||||
);
|
||||
@ -29,6 +51,7 @@ gulp.task(
|
||||
"clean-hassio",
|
||||
"gen-icons-json",
|
||||
env.useRollup() ? "rollup-prod-hassio" : "webpack-prod-hassio",
|
||||
writeEntrypointJS,
|
||||
...// Don't compress running tests
|
||||
(env.isTest() ? [] : ["compress-hassio"])
|
||||
)
|
||||
|
@ -92,11 +92,7 @@ gulp.task("rollup-watch-app", () => {
|
||||
});
|
||||
|
||||
gulp.task("rollup-watch-hassio", () => {
|
||||
watchRollup(
|
||||
// Force latestBuild = false for hassio config.
|
||||
(conf) => rollupConfig.createHassioConfig({ ...conf, latestBuild: false }),
|
||||
["hassio/src/**"]
|
||||
);
|
||||
watchRollup(rollupConfig.createHassioConfig, ["hassio/src/**"]);
|
||||
});
|
||||
|
||||
gulp.task("rollup-dev-server-demo", () => {
|
||||
@ -137,12 +133,7 @@ gulp.task(
|
||||
);
|
||||
|
||||
gulp.task("rollup-prod-hassio", () =>
|
||||
buildRollup(
|
||||
rollupConfig.createHassioConfig({
|
||||
isProdBuild: true,
|
||||
latestBuild: false,
|
||||
})
|
||||
)
|
||||
bothBuilds(rollupConfig.createHassioConfig, { isProdBuild: true })
|
||||
);
|
||||
|
||||
gulp.task("rollup-prod-gallery", () =>
|
||||
|
@ -129,7 +129,7 @@ gulp.task("webpack-watch-hassio", () => {
|
||||
webpack(
|
||||
createHassioConfig({
|
||||
isProdBuild: false,
|
||||
latestBuild: false,
|
||||
latestBuild: true,
|
||||
})
|
||||
).watch({}, handler());
|
||||
});
|
||||
@ -139,9 +139,8 @@ gulp.task(
|
||||
() =>
|
||||
new Promise((resolve) =>
|
||||
webpack(
|
||||
createHassioConfig({
|
||||
bothBuilds(createHassioConfig, {
|
||||
isProdBuild: true,
|
||||
latestBuild: false,
|
||||
}),
|
||||
handler(resolve)
|
||||
)
|
||||
|
@ -34,7 +34,7 @@ module.exports = {
|
||||
|
||||
hassio_dir: path.resolve(__dirname, "../hassio"),
|
||||
hassio_output_root: path.resolve(__dirname, "../hassio/build"),
|
||||
hassio_publicPath: "/api/hassio/app/",
|
||||
hassio_publicPath: "/api/hassio/app",
|
||||
|
||||
translations_src: path.resolve(__dirname, "../src/translations"),
|
||||
};
|
||||
|
@ -70,7 +70,9 @@ const createWebpackConfig = ({
|
||||
if (
|
||||
!context.includes("/node_modules/") ||
|
||||
// calling define.amd will call require("!!webpack amd options")
|
||||
resource.startsWith("!!webpack")
|
||||
resource.startsWith("!!webpack") ||
|
||||
// loaded by webpack dev server but doesn't exist.
|
||||
resource === "webpack/hot"
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
@ -80,7 +82,11 @@ const createWebpackConfig = ({
|
||||
? path.resolve(context, resource)
|
||||
: require.resolve(resource);
|
||||
} catch (err) {
|
||||
console.error("Error in ignore plugin", resource, context);
|
||||
console.error(
|
||||
"Error in Home Assistant ignore plugin",
|
||||
resource,
|
||||
context
|
||||
);
|
||||
throw err;
|
||||
}
|
||||
|
||||
|
@ -45,7 +45,6 @@
|
||||
(function() {
|
||||
// // Safari 10.1 supports type=module but ignores nomodule, so we add this check.
|
||||
if (!isS101) {
|
||||
_ls("/static/polyfills/custom-elements-es5-adapter.js");
|
||||
<% if (useRollup) { %>
|
||||
_ls("/static/js/s.min.js").onload = function() {
|
||||
System.import("<%= es5LauncherJS %>");
|
||||
|
@ -36,7 +36,6 @@
|
||||
(function() {
|
||||
// // Safari 10.1 supports type=module but ignores nomodule, so we add this check.
|
||||
if (!isS101) {
|
||||
_ls("/static/polyfills/custom-elements-es5-adapter.js");
|
||||
<% if (useRollup) { %>
|
||||
_ls("/static/js/s.min.js").onload = function() {
|
||||
System.import("<%= es5LauncherJS %>");
|
||||
|
@ -1,11 +1,8 @@
|
||||
const { createCastConfig } = require("../build-scripts/webpack.js");
|
||||
const { isProdBuild } = require("../build-scripts/env.js");
|
||||
|
||||
// File just used for stats builds
|
||||
|
||||
const latestBuild = true;
|
||||
const { isProdBuild, isStatsBuild } = require("../build-scripts/env.js");
|
||||
|
||||
module.exports = createCastConfig({
|
||||
isProdBuild: isProdBuild(),
|
||||
latestBuild,
|
||||
isStatsBuild: isStatsBuild(),
|
||||
latestBuild: true,
|
||||
});
|
||||
|
@ -92,7 +92,6 @@
|
||||
(function() {
|
||||
// // Safari 10.1 supports type=module but ignores nomodule, so we add this check.
|
||||
if (!isS101) {
|
||||
_ls("/static/polyfills/custom-elements-es5-adapter.js");
|
||||
<% if (useRollup) { %>
|
||||
_ls("/static/js/s.min.js").onload = function() {
|
||||
System.import("<%= es5DemoJS %>");
|
||||
|
@ -3,6 +3,7 @@ import { html } from "@polymer/polymer/lib/utils/html-tag";
|
||||
/* eslint-plugin-disable lit */
|
||||
import { PolymerElement } from "@polymer/polymer/polymer-element";
|
||||
import "../../../src/components/ha-switch";
|
||||
import "../../../src/components/ha-formfield";
|
||||
import "./demo-card";
|
||||
|
||||
class DemoCards extends PolymerElement {
|
||||
@ -26,9 +27,10 @@ class DemoCards extends PolymerElement {
|
||||
</style>
|
||||
<app-toolbar>
|
||||
<div class="filters">
|
||||
<ha-switch checked="[[_showConfig]]" on-change="_showConfigToggled">
|
||||
Show config
|
||||
</ha-switch>
|
||||
<ha-formfield label="Show config">
|
||||
<ha-switch checked="[[_showConfig]]" on-change="_showConfigToggled">
|
||||
</ha-switch>
|
||||
</ha-formfield>
|
||||
</div>
|
||||
</app-toolbar>
|
||||
<div class="cards">
|
||||
|
@ -1,5 +1,8 @@
|
||||
const { createGalleryConfig } = require("../build-scripts/webpack.js");
|
||||
const { isProdBuild, isStatsBuild } = require("../build-scripts/env.js");
|
||||
|
||||
module.exports = createGalleryConfig({
|
||||
isProdBuild: isProdBuild(),
|
||||
isStatsBuild: isStatsBuild(),
|
||||
latestBuild: true,
|
||||
});
|
||||
|
@ -1,9 +1,6 @@
|
||||
window.loadES5Adapter().then(() => {
|
||||
// eslint-disable-next-line
|
||||
import(/* webpackChunkName: "roboto" */ "../../src/resources/roboto");
|
||||
// eslint-disable-next-line
|
||||
import(/* webpackChunkName: "hassio-main" */ "./hassio-main");
|
||||
});
|
||||
import "../../src/resources/compatibility";
|
||||
import "../../src/resources/roboto";
|
||||
import "./hassio-main";
|
||||
|
||||
const styleEl = document.createElement("style");
|
||||
styleEl.innerHTML = `
|
||||
|
@ -1,4 +1,3 @@
|
||||
import "@polymer/paper-menu-button/paper-menu-button";
|
||||
import {
|
||||
css,
|
||||
CSSResult,
|
||||
|
@ -1,11 +1,8 @@
|
||||
const { createHassioConfig } = require("../build-scripts/webpack.js");
|
||||
const { isProdBuild } = require("../build-scripts/env.js");
|
||||
|
||||
// File just used for stats builds
|
||||
|
||||
const latestBuild = false;
|
||||
const { isProdBuild, isStatsBuild } = require("../build-scripts/env.js");
|
||||
|
||||
module.exports = createHassioConfig({
|
||||
isProdBuild: isProdBuild(),
|
||||
latestBuild,
|
||||
isStatsBuild: isStatsBuild(),
|
||||
latestBuild: true,
|
||||
});
|
||||
|
@ -17,9 +17,7 @@
|
||||
"lint": "npm run lint:eslint && npm run lint:prettier && npm run lint:types",
|
||||
"format": "npm run format:eslint && npm run format:prettier",
|
||||
"mocha": "node_modules/.bin/ts-mocha -p test-mocha/tsconfig.test.json --opts test-mocha/mocha.opts",
|
||||
"test": "npm run lint && npm run mocha",
|
||||
"docker_build": "sh ./script/docker_run.sh build $npm_package_version",
|
||||
"bash": "sh ./script/docker_run.sh bash $npm_package_version"
|
||||
"test": "npm run lint && npm run mocha"
|
||||
},
|
||||
"author": "Paulus Schoutsen <Paulus@PaulusSchoutsen.nl> (http://paulusschoutsen.nl)",
|
||||
"license": "Apache-2.0",
|
||||
@ -89,7 +87,7 @@
|
||||
"fuse.js": "^6.0.0",
|
||||
"google-timezones-json": "^1.0.2",
|
||||
"hls.js": "^0.12.4",
|
||||
"home-assistant-js-websocket": "^5.2.1",
|
||||
"home-assistant-js-websocket": "^5.3.0",
|
||||
"idb-keyval": "^3.2.0",
|
||||
"intl-messageformat": "^8.3.9",
|
||||
"js-yaml": "^3.13.1",
|
||||
|
@ -1,14 +0,0 @@
|
||||
#!/bin/bash
|
||||
# Docker entry point inspired by travis build and script/build_frontend
|
||||
|
||||
# Stop on errors
|
||||
set -e
|
||||
|
||||
# Build the frontend but not used the npm run build
|
||||
/bin/bash script/build_frontend
|
||||
|
||||
# TEST
|
||||
npm run test
|
||||
|
||||
#
|
||||
#xvfb-run wct
|
@ -1,103 +0,0 @@
|
||||
#!/bin/bash
|
||||
# Basic Docker Management scripts
|
||||
# With this script you can build software, or enter an agnostic development environment and run commands interactively.
|
||||
|
||||
|
||||
|
||||
check_mandatory_tools(){
|
||||
if [ "x$(which docker)" == "x" ]; then
|
||||
echo "UNKNOWN - Missing docker binary! Are you sure it is installed and reachable?"
|
||||
exit 3
|
||||
fi
|
||||
|
||||
docker info > /dev/null 2>&1
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "UNKNOWN - Unable to talk to the docker daemon! Maybe the docker daemon is not running"
|
||||
exit 3
|
||||
fi
|
||||
}
|
||||
|
||||
check_dev_image(){
|
||||
if [[ "$(docker images -q ${IMAGE_NAME}:$IMAGE_TAG 2> /dev/null)" == "" ]]; then
|
||||
echo "UNKNOWN - Can't find the development docker image ${IMAGE_NAME}:$IMAGE_TAG"
|
||||
while true; do
|
||||
read -p "Do you want to create it now?" yn
|
||||
case $yn in
|
||||
[Yy]* ) create_image; break;;
|
||||
[Nn]* ) exit 3;;
|
||||
* ) echo "Please answer y or n";;
|
||||
esac
|
||||
done
|
||||
fi
|
||||
}
|
||||
|
||||
# Building the basic image for compiling the production frontend
|
||||
create_image(){
|
||||
docker build -t ${IMAGE_NAME}:${IMAGE_TAG} .
|
||||
}
|
||||
|
||||
#
|
||||
# Execute interactive bash on basic image
|
||||
#
|
||||
run_bash_on_docker(){
|
||||
|
||||
check_dev_image
|
||||
|
||||
docker run -it \
|
||||
-v $PWD/:/frontend/ \
|
||||
-v /frontend/node_modules \
|
||||
-v /frontend/bower_components \
|
||||
${IMAGE_NAME}:${IMAGE_TAG} /bin/bash
|
||||
}
|
||||
|
||||
#
|
||||
# Execute the basic image for compiling the production frontend
|
||||
#
|
||||
build_all(){
|
||||
|
||||
check_dev_image
|
||||
|
||||
docker run -it \
|
||||
-v $PWD/:/frontend/ \
|
||||
-v /frontend/node_modules \
|
||||
-v /frontend/bower_components \
|
||||
${IMAGE_NAME}:${IMAGE_TAG} /bin/bash script/build_frontend
|
||||
|
||||
}
|
||||
|
||||
# Init Global Variable
|
||||
IMAGE_NAME=home_assistant_fe_image
|
||||
IMAGE_TAG=${2:-latest}
|
||||
|
||||
check_mandatory_tools
|
||||
|
||||
case "$1" in
|
||||
setup_env)
|
||||
create_image
|
||||
;;
|
||||
bash)
|
||||
run_bash_on_docker
|
||||
;;
|
||||
build)
|
||||
build_all
|
||||
;;
|
||||
*)
|
||||
echo "NAME"
|
||||
echo " Docker Management."
|
||||
echo ""
|
||||
echo "SYNOPSIS"
|
||||
echo " ${0} command [version]"
|
||||
echo ""
|
||||
echo "DESCRIPTION"
|
||||
echo " With this script you can build software, or enter an agnostic development environment and run commands interactively."
|
||||
echo ""
|
||||
echo " The command are:"
|
||||
echo " setup_env Create develop images"
|
||||
echo " bash Run bash on develop enviroments"
|
||||
echo " build Run silent build"
|
||||
echo ""
|
||||
echo " The version is optional, if not inserted it assumes \"latest\". "
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
exit 0
|
2
setup.py
2
setup.py
@ -2,7 +2,7 @@ from setuptools import setup, find_packages
|
||||
|
||||
setup(
|
||||
name="home-assistant-frontend",
|
||||
version="20200603.3",
|
||||
version="20200613.0",
|
||||
description="The Home Assistant frontend",
|
||||
url="https://github.com/home-assistant/home-assistant-polymer",
|
||||
author="The Home Assistant Authors",
|
||||
|
@ -6,7 +6,7 @@ export const isValidEntityId = (entityId: string) =>
|
||||
export const createValidEntityId = (input: string) =>
|
||||
input
|
||||
.toLowerCase()
|
||||
.replace(/\s|'/g, "_") // replace spaces and quotes with underscore
|
||||
.replace(/\s|'|\./g, "_") // replace spaces, points and quotes with underscore
|
||||
.replace(/\W/g, "") // remove not allowed chars
|
||||
.replace(/_{2,}/g, "_") // replace multiple underscores with 1
|
||||
.replace(/_$/, ""); // remove underscores at the end
|
||||
|
@ -57,9 +57,12 @@ export const iconColorCSS = css`
|
||||
0% {
|
||||
opacity: 1;
|
||||
}
|
||||
100% {
|
||||
50% {
|
||||
opacity: 0;
|
||||
}
|
||||
100% {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
ha-icon[data-domain="plant"][data-state="problem"],
|
||||
|
@ -619,6 +619,11 @@ export class HaDataTable extends LitElement {
|
||||
text-transform: inherit;
|
||||
}
|
||||
|
||||
.mdc-data-table__cell a {
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.mdc-data-table__cell--numeric {
|
||||
text-align: right;
|
||||
}
|
||||
|
@ -40,7 +40,7 @@ export class HaButtonMenu extends LitElement {
|
||||
static get styles(): CSSResult {
|
||||
return css`
|
||||
:host {
|
||||
display: block;
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
}
|
||||
`;
|
||||
|
@ -176,7 +176,9 @@ class HaCameraStream extends LitElement {
|
||||
Hls: HLSModule,
|
||||
url: string
|
||||
) {
|
||||
const hls = new Hls();
|
||||
const hls = new Hls({
|
||||
liveBackBufferLength: 60,
|
||||
});
|
||||
this._hlsPolyfillInstance = hls;
|
||||
hls.attachMedia(videoEl);
|
||||
hls.on(Hls.Events.MEDIA_ATTACHED, () => {
|
||||
|
@ -12,6 +12,8 @@ import {
|
||||
class HaCard extends LitElement {
|
||||
@property() public header?: string;
|
||||
|
||||
@property({ type: Boolean, reflect: true }) public outlined = false;
|
||||
|
||||
static get styles(): CSSResult {
|
||||
return css`
|
||||
:host {
|
||||
@ -19,12 +21,12 @@ class HaCard extends LitElement {
|
||||
--ha-card-background,
|
||||
var(--paper-card-background-color, white)
|
||||
);
|
||||
border-radius: var(--ha-card-border-radius, 2px);
|
||||
border-radius: var(--ha-card-border-radius, 4px);
|
||||
box-shadow: var(
|
||||
--ha-card-box-shadow,
|
||||
0 2px 2px 0 rgba(0, 0, 0, 0.14),
|
||||
0 1px 5px 0 rgba(0, 0, 0, 0.12),
|
||||
0 3px 1px -2px rgba(0, 0, 0, 0.2)
|
||||
0px 2px 1px -1px rgba(0, 0, 0, 0.2),
|
||||
0px 1px 1px 0px rgba(0, 0, 0, 0.14),
|
||||
0px 1px 3px 0px rgba(0, 0, 0, 0.12)
|
||||
);
|
||||
color: var(--primary-text-color);
|
||||
display: block;
|
||||
@ -32,6 +34,16 @@ class HaCard extends LitElement {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
:host([outlined]) {
|
||||
box-shadow: 0 0 0 0;
|
||||
border-width: 1px;
|
||||
border-style: solid;
|
||||
border-color: var(
|
||||
--ha-card-border-color,
|
||||
var(--divider-color, #e0e0e0)
|
||||
);
|
||||
}
|
||||
|
||||
.card-header,
|
||||
:host ::slotted(.card-header) {
|
||||
color: var(--ha-card-header-color, --primary-text-color);
|
||||
|
@ -13,7 +13,7 @@ export const createCloseHeading = (hass: HomeAssistant, title: string) => html`
|
||||
<mwc-icon-button
|
||||
aria-label=${hass.localize("ui.dialogs.generic.close")}
|
||||
dialogAction="close"
|
||||
class="close_button"
|
||||
class="header_button"
|
||||
>
|
||||
<ha-svg-icon path=${mdiClose}></ha-svg-icon>
|
||||
</mwc-icon-button>
|
||||
@ -25,6 +25,9 @@ export class HaDialog extends MwcDialog {
|
||||
return [
|
||||
style,
|
||||
css`
|
||||
.mdc-dialog {
|
||||
z-index: var(--dialog-z-index, 7);
|
||||
}
|
||||
.mdc-dialog__actions {
|
||||
justify-content: var(--justify-action-buttons, flex-end);
|
||||
}
|
||||
@ -35,10 +38,15 @@ export class HaDialog extends MwcDialog {
|
||||
display: block;
|
||||
height: 20px;
|
||||
}
|
||||
.close_button {
|
||||
.mdc-dialog__content {
|
||||
padding: var(--dialog-content-padding, 20px 24px);
|
||||
}
|
||||
.header_button {
|
||||
position: absolute;
|
||||
right: 16px;
|
||||
top: 12px;
|
||||
text-decoration: none;
|
||||
color: inherit;
|
||||
}
|
||||
`,
|
||||
];
|
||||
|
33
src/components/ha-formfield.ts
Normal file
33
src/components/ha-formfield.ts
Normal file
@ -0,0 +1,33 @@
|
||||
import "@material/mwc-formfield";
|
||||
import type { Formfield } from "@material/mwc-formfield";
|
||||
import { style } from "@material/mwc-formfield/mwc-formfield-css";
|
||||
import { css, CSSResult, customElement } from "lit-element";
|
||||
import { Constructor } from "../types";
|
||||
|
||||
const MwcFormfield = customElements.get("mwc-formfield") as Constructor<
|
||||
Formfield
|
||||
>;
|
||||
|
||||
@customElement("ha-formfield")
|
||||
export class HaFormfield extends MwcFormfield {
|
||||
protected static get styles(): CSSResult[] {
|
||||
return [
|
||||
style,
|
||||
css`
|
||||
::slotted(ha-switch) {
|
||||
margin-right: 10px;
|
||||
}
|
||||
[dir="rtl"] ::slotted(ha-switch),
|
||||
::slotted(ha-switch)[dir="rtl"] {
|
||||
margin-left: 10px;
|
||||
}
|
||||
`,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"ha-formfield": HaFormfield;
|
||||
}
|
||||
}
|
@ -1,17 +1,37 @@
|
||||
import { HaIconButton } from "./ha-icon-button";
|
||||
import {
|
||||
LitElement,
|
||||
property,
|
||||
TemplateResult,
|
||||
html,
|
||||
customElement,
|
||||
} from "lit-element";
|
||||
import { mdiArrowLeft, mdiArrowRight } from "@mdi/js";
|
||||
import "@material/mwc-icon-button/mwc-icon-button";
|
||||
import "./ha-svg-icon";
|
||||
|
||||
@customElement("ha-icon-button-arrow-next")
|
||||
export class HaIconButtonArrowNext extends LitElement {
|
||||
@property({ type: Boolean }) public disabled = false;
|
||||
|
||||
@property() private _icon = mdiArrowRight;
|
||||
|
||||
export class HaIconButtonArrowNext extends HaIconButton {
|
||||
public connectedCallback() {
|
||||
super.connectedCallback();
|
||||
|
||||
// wait to check for direction since otherwise direction is wrong even though top level is RTL
|
||||
setTimeout(() => {
|
||||
this.icon =
|
||||
this._icon =
|
||||
window.getComputedStyle(this).direction === "ltr"
|
||||
? "hass:arrow-right"
|
||||
: "hass:arrow-left";
|
||||
? mdiArrowRight
|
||||
: mdiArrowLeft;
|
||||
}, 100);
|
||||
}
|
||||
|
||||
protected render(): TemplateResult {
|
||||
return html`<mwc-icon-button .disabled=${this.disabled}>
|
||||
<ha-svg-icon .path=${this._icon}></ha-svg-icon>
|
||||
</mwc-icon-button> `;
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
@ -19,5 +39,3 @@ declare global {
|
||||
"ha-icon-button-arrow-next": HaIconButtonArrowNext;
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define("ha-icon-button-arrow-next", HaIconButtonArrowNext);
|
||||
|
@ -1,8 +1,15 @@
|
||||
import { LitElement, property, TemplateResult, html } from "lit-element";
|
||||
import {
|
||||
LitElement,
|
||||
property,
|
||||
TemplateResult,
|
||||
html,
|
||||
customElement,
|
||||
} from "lit-element";
|
||||
import { mdiArrowLeft, mdiArrowRight } from "@mdi/js";
|
||||
import "@material/mwc-icon-button/mwc-icon-button";
|
||||
import "./ha-svg-icon";
|
||||
|
||||
@customElement("ha-icon-button-arrow-prev")
|
||||
export class HaIconButtonArrowPrev extends LitElement {
|
||||
@property({ type: Boolean }) public disabled = false;
|
||||
|
||||
@ -32,5 +39,3 @@ declare global {
|
||||
"ha-icon-button-arrow-prev": HaIconButtonArrowPrev;
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define("ha-icon-button-arrow-prev", HaIconButtonArrowPrev);
|
||||
|
@ -1,17 +1,37 @@
|
||||
import { HaIconButton } from "./ha-icon-button";
|
||||
import {
|
||||
LitElement,
|
||||
property,
|
||||
TemplateResult,
|
||||
html,
|
||||
customElement,
|
||||
} from "lit-element";
|
||||
import { mdiChevronRight, mdiChevronLeft } from "@mdi/js";
|
||||
import "@material/mwc-icon-button";
|
||||
import "./ha-svg-icon";
|
||||
|
||||
@customElement("ha-icon-button-next")
|
||||
export class HaIconButtonNext extends LitElement {
|
||||
@property({ type: Boolean }) public disabled = false;
|
||||
|
||||
@property() private _icon = mdiChevronRight;
|
||||
|
||||
export class HaIconButtonNext extends HaIconButton {
|
||||
public connectedCallback() {
|
||||
super.connectedCallback();
|
||||
|
||||
// wait to check for direction since otherwise direction is wrong even though top level is RTL
|
||||
setTimeout(() => {
|
||||
this.icon =
|
||||
this._icon =
|
||||
window.getComputedStyle(this).direction === "ltr"
|
||||
? "hass:chevron-right"
|
||||
: "hass:chevron-left";
|
||||
? mdiChevronRight
|
||||
: mdiChevronLeft;
|
||||
}, 100);
|
||||
}
|
||||
|
||||
protected render(): TemplateResult {
|
||||
return html`<mwc-icon-button .disabled=${this.disabled}>
|
||||
<ha-svg-icon .path=${this._icon}></ha-svg-icon>
|
||||
</mwc-icon-button> `;
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
@ -19,5 +39,3 @@ declare global {
|
||||
"ha-icon-button-next": HaIconButtonNext;
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define("ha-icon-button-next", HaIconButtonNext);
|
||||
|
@ -1,17 +1,37 @@
|
||||
import { HaIconButton } from "./ha-icon-button";
|
||||
import {
|
||||
LitElement,
|
||||
property,
|
||||
TemplateResult,
|
||||
html,
|
||||
customElement,
|
||||
} from "lit-element";
|
||||
import { mdiChevronRight, mdiChevronLeft } from "@mdi/js";
|
||||
import "@material/mwc-icon-button/mwc-icon-button";
|
||||
import "./ha-svg-icon";
|
||||
|
||||
@customElement("ha-icon-button-prev")
|
||||
export class HaIconButtonPrev extends LitElement {
|
||||
@property({ type: Boolean }) public disabled = false;
|
||||
|
||||
@property() private _icon = mdiChevronLeft;
|
||||
|
||||
export class HaIconButtonPrev extends HaIconButton {
|
||||
public connectedCallback() {
|
||||
super.connectedCallback();
|
||||
|
||||
// wait to check for direction since otherwise direction is wrong even though top level is RTL
|
||||
setTimeout(() => {
|
||||
this.icon =
|
||||
this._icon =
|
||||
window.getComputedStyle(this).direction === "ltr"
|
||||
? "hass:chevron-left"
|
||||
: "hass:chevron-right";
|
||||
? mdiChevronLeft
|
||||
: mdiChevronRight;
|
||||
}, 100);
|
||||
}
|
||||
|
||||
protected render(): TemplateResult {
|
||||
return html`<mwc-icon-button .disabled=${this.disabled}>
|
||||
<ha-svg-icon .path=${this._icon}></ha-svg-icon>
|
||||
</mwc-icon-button> `;
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
@ -19,5 +39,3 @@ declare global {
|
||||
"ha-icon-button-prev": HaIconButtonPrev;
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define("ha-icon-button-prev", HaIconButtonPrev);
|
||||
|
@ -13,7 +13,7 @@ class HaMarkdownElement extends UpdatingElement {
|
||||
protected update(changedProps) {
|
||||
super.update(changedProps);
|
||||
if (this.content !== undefined) {
|
||||
this._render();
|
||||
this._render();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,3 +1,5 @@
|
||||
import "@material/mwc-icon-button";
|
||||
import { mdiMenu } from "@mdi/js";
|
||||
import { UnsubscribeFunc } from "home-assistant-js-websocket";
|
||||
import {
|
||||
css,
|
||||
@ -12,8 +14,7 @@ import { fireEvent } from "../common/dom/fire_event";
|
||||
import { computeDomain } from "../common/entity/compute_domain";
|
||||
import { subscribeNotifications } from "../data/persistent_notification";
|
||||
import { HomeAssistant } from "../types";
|
||||
import "./ha-icon-button";
|
||||
import { mdiMenu } from "@mdi/js";
|
||||
import "./ha-svg-icon";
|
||||
|
||||
@customElement("ha-menu-button")
|
||||
class HaMenuButton extends LitElement {
|
||||
|
@ -1,6 +1,12 @@
|
||||
import { mdiBell, mdiCellphoneSettingsVariant } from "@mdi/js";
|
||||
import "@material/mwc-icon-button";
|
||||
import {
|
||||
mdiBell,
|
||||
mdiCellphoneSettingsVariant,
|
||||
mdiMenuOpen,
|
||||
mdiMenu,
|
||||
mdiViewDashboard,
|
||||
} from "@mdi/js";
|
||||
import "@polymer/app-layout/app-toolbar/app-toolbar";
|
||||
import "./ha-icon-button";
|
||||
import "@polymer/paper-item/paper-icon-item";
|
||||
import type { PaperIconItemElement } from "@polymer/paper-item/paper-icon-item";
|
||||
import "@polymer/paper-item/paper-item";
|
||||
@ -29,9 +35,9 @@ import {
|
||||
getExternalConfig,
|
||||
} from "../external_app/external_config";
|
||||
import type { HomeAssistant, PanelInfo } from "../types";
|
||||
import "./ha-svg-icon";
|
||||
import "./ha-icon";
|
||||
import "./ha-menu-button";
|
||||
import "./ha-svg-icon";
|
||||
import "./user/ha-user-badge";
|
||||
|
||||
const SHOW_AFTER_SPACER = ["config", "developer-tools", "hassio"];
|
||||
@ -153,13 +159,16 @@ class HaSidebar extends LitElement {
|
||||
<div class="menu">
|
||||
${!this.narrow
|
||||
? html`
|
||||
<ha-icon-button
|
||||
aria-label=${hass.localize("ui.sidebar.sidebar_toggle")}
|
||||
.icon=${hass.dockedSidebar === "docked"
|
||||
? "hass:menu-open"
|
||||
: "hass:menu"}
|
||||
<mwc-icon-button
|
||||
.label=${hass.localize("ui.sidebar.sidebar_toggle")}
|
||||
@click=${this._toggleSidebar}
|
||||
></ha-icon-button>
|
||||
>
|
||||
<ha-svg-icon
|
||||
.path=${hass.dockedSidebar === "docked"
|
||||
? mdiMenuOpen
|
||||
: mdiMenu}
|
||||
></ha-svg-icon>
|
||||
</mwc-icon-button>
|
||||
`
|
||||
: ""}
|
||||
<span class="title">Home Assistant</span>
|
||||
@ -174,14 +183,16 @@ class HaSidebar extends LitElement {
|
||||
>
|
||||
${this._renderPanel(
|
||||
defaultPanel.url_path,
|
||||
defaultPanel.icon || "hass:view-dashboard",
|
||||
defaultPanel.title || hass.localize("panel.states")
|
||||
defaultPanel.title || hass.localize("panel.states"),
|
||||
defaultPanel.icon,
|
||||
!defaultPanel.icon ? mdiViewDashboard : undefined
|
||||
)}
|
||||
${beforeSpacer.map((panel) =>
|
||||
this._renderPanel(
|
||||
panel.url_path,
|
||||
hass.localize(`panel.${panel.title}`) || panel.title,
|
||||
panel.icon,
|
||||
hass.localize(`panel.${panel.title}`) || panel.title
|
||||
undefined
|
||||
)
|
||||
)}
|
||||
<div class="spacer" disabled></div>
|
||||
@ -189,8 +200,9 @@ class HaSidebar extends LitElement {
|
||||
${afterSpacer.map((panel) =>
|
||||
this._renderPanel(
|
||||
panel.url_path,
|
||||
hass.localize(`panel.${panel.title}`) || panel.title,
|
||||
panel.icon,
|
||||
hass.localize(`panel.${panel.title}`) || panel.title
|
||||
undefined
|
||||
)
|
||||
)}
|
||||
${this._externalConfig && this._externalConfig.hasSettingsScreen
|
||||
@ -443,7 +455,12 @@ class HaSidebar extends LitElement {
|
||||
fireEvent(this, "hass-toggle-menu");
|
||||
}
|
||||
|
||||
private _renderPanel(urlPath, icon, title) {
|
||||
private _renderPanel(
|
||||
urlPath: string,
|
||||
title: string | null,
|
||||
icon?: string | null,
|
||||
iconPath?: string | null
|
||||
) {
|
||||
return html`
|
||||
<a
|
||||
aria-role="option"
|
||||
@ -454,7 +471,12 @@ class HaSidebar extends LitElement {
|
||||
@mouseleave=${this._itemMouseLeave}
|
||||
>
|
||||
<paper-icon-item>
|
||||
<ha-icon slot="item-icon" .icon="${icon}"></ha-icon>
|
||||
${iconPath
|
||||
? html`<ha-svg-icon
|
||||
slot="item-icon"
|
||||
.path=${iconPath}
|
||||
></ha-svg-icon>`
|
||||
: html`<ha-icon slot="item-icon" .icon=${icon}></ha-icon>`}
|
||||
<span class="item-text">${title}</span>
|
||||
</paper-icon-item>
|
||||
</a>
|
||||
@ -496,13 +518,13 @@ class HaSidebar extends LitElement {
|
||||
width: 256px;
|
||||
}
|
||||
|
||||
.menu ha-icon-button {
|
||||
.menu mwc-icon-button {
|
||||
color: var(--sidebar-icon-color);
|
||||
}
|
||||
:host([expanded]) .menu ha-icon-button {
|
||||
:host([expanded]) .menu mwc-icon-button {
|
||||
margin-right: 23px;
|
||||
}
|
||||
:host([expanded][_rtl]) .menu ha-icon-button {
|
||||
:host([expanded][_rtl]) .menu mwc-icon-button {
|
||||
margin-right: 0px;
|
||||
margin-left: 23px;
|
||||
}
|
||||
@ -714,7 +736,7 @@ class HaSidebar extends LitElement {
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
:host([_rtl]) .menu ha-icon-button {
|
||||
:host([_rtl]) .menu mwc-icon-button {
|
||||
-webkit-transform: scaleX(-1);
|
||||
transform: scaleX(-1);
|
||||
}
|
||||
|
@ -1,15 +1,7 @@
|
||||
import { ripple } from "@material/mwc-ripple/ripple-directive";
|
||||
import "@material/mwc-switch";
|
||||
import type { Switch } from "@material/mwc-switch";
|
||||
import { style } from "@material/mwc-switch/mwc-switch-css";
|
||||
import {
|
||||
css,
|
||||
CSSResult,
|
||||
customElement,
|
||||
html,
|
||||
property,
|
||||
query,
|
||||
} from "lit-element";
|
||||
import { css, CSSResult, customElement, property } from "lit-element";
|
||||
import { forwardHaptic } from "../data/haptics";
|
||||
import { Constructor } from "../types";
|
||||
|
||||
@ -22,18 +14,12 @@ export class HaSwitch extends MwcSwitch {
|
||||
// Do not add haptic when a user is required to press save.
|
||||
@property({ type: Boolean }) public haptic = false;
|
||||
|
||||
@query("slot") private _slot!: HTMLSlotElement;
|
||||
|
||||
protected firstUpdated() {
|
||||
super.firstUpdated();
|
||||
this.style.setProperty(
|
||||
"--mdc-theme-secondary",
|
||||
"var(--switch-checked-color)"
|
||||
);
|
||||
this.classList.toggle(
|
||||
"slotted",
|
||||
Boolean(this._slot.assignedNodes().length)
|
||||
);
|
||||
this.addEventListener("change", () => {
|
||||
if (this.haptic) {
|
||||
forwardHaptic("light");
|
||||
@ -41,40 +27,10 @@ export class HaSwitch extends MwcSwitch {
|
||||
});
|
||||
}
|
||||
|
||||
protected render() {
|
||||
return html`
|
||||
<div class="mdc-switch">
|
||||
<div class="mdc-switch__track"></div>
|
||||
<div
|
||||
class="mdc-switch__thumb-underlay"
|
||||
.ripple="${ripple({
|
||||
interactionNode: this,
|
||||
})}"
|
||||
>
|
||||
<div class="mdc-switch__thumb">
|
||||
<input
|
||||
type="checkbox"
|
||||
id="basic-switch"
|
||||
class="mdc-switch__native-control"
|
||||
role="switch"
|
||||
@change="${this._haChangeHandler}"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<label for="basic-switch"><slot></slot></label>
|
||||
`;
|
||||
}
|
||||
|
||||
protected static get styles(): CSSResult[] {
|
||||
return [
|
||||
style,
|
||||
css`
|
||||
:host {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
}
|
||||
.mdc-switch.mdc-switch--checked .mdc-switch__thumb {
|
||||
background-color: var(--switch-checked-button-color);
|
||||
border-color: var(--switch-checked-button-color);
|
||||
@ -91,18 +47,9 @@ export class HaSwitch extends MwcSwitch {
|
||||
background-color: var(--switch-unchecked-track-color);
|
||||
border-color: var(--switch-unchecked-track-color);
|
||||
}
|
||||
:host(.slotted) .mdc-switch {
|
||||
margin-right: 24px;
|
||||
}
|
||||
`,
|
||||
];
|
||||
}
|
||||
|
||||
private _haChangeHandler(e: Event) {
|
||||
this.mdcFoundation.handleChange(e);
|
||||
// catch "click" event and sync properties
|
||||
this.checked = this.formElement.checked;
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
|
@ -1,46 +0,0 @@
|
||||
/*
|
||||
Wrapper for paper-textarea.
|
||||
|
||||
paper-textarea crashes on iOS when created programmatically. This only impacts
|
||||
our automation and script editors as they are using Preact. Polymer is using
|
||||
template elements and does not have this issue.
|
||||
|
||||
paper-textarea issue: https://github.com/PolymerElements/paper-input/issues/556
|
||||
WebKit issue: https://bugs.webkit.org/show_bug.cgi?id=174629
|
||||
*/
|
||||
|
||||
import "@polymer/paper-input/paper-textarea";
|
||||
import { html } from "@polymer/polymer/lib/utils/html-tag";
|
||||
/* eslint-plugin-disable lit */
|
||||
import { PolymerElement } from "@polymer/polymer/polymer-element";
|
||||
|
||||
class HaTextarea extends PolymerElement {
|
||||
static get template() {
|
||||
return html`
|
||||
<style>
|
||||
:host {
|
||||
display: block;
|
||||
}
|
||||
</style>
|
||||
<paper-textarea
|
||||
label="[[label]]"
|
||||
placeholder="[[placeholder]]"
|
||||
value="{{value}}"
|
||||
></paper-textarea>
|
||||
`;
|
||||
}
|
||||
|
||||
static get properties() {
|
||||
return {
|
||||
name: String,
|
||||
label: String,
|
||||
placeholder: String,
|
||||
value: {
|
||||
type: String,
|
||||
notify: true,
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define("ha-textarea", HaTextarea);
|
@ -12,6 +12,11 @@ export interface ConfigUpdateValues {
|
||||
internal_url?: string | null;
|
||||
}
|
||||
|
||||
export interface CheckConfigResult {
|
||||
result: "valid" | "invalid";
|
||||
errors: string | null;
|
||||
}
|
||||
|
||||
export const saveCoreConfig = (
|
||||
hass: HomeAssistant,
|
||||
values: Partial<ConfigUpdateValues>
|
||||
@ -25,3 +30,6 @@ export const detectCoreConfig = (hass: HomeAssistant) =>
|
||||
hass.callWS<Partial<ConfigUpdateValues>>({
|
||||
type: "config/core/detect",
|
||||
});
|
||||
|
||||
export const checkCoreConfig = (hass: HomeAssistant) =>
|
||||
hass.callApi<CheckConfigResult>("POST", "config/core/check_config");
|
||||
|
@ -11,6 +11,7 @@ import {
|
||||
} from "lit-element";
|
||||
import "../../components/dialog/ha-paper-dialog";
|
||||
import "../../components/ha-switch";
|
||||
import "../../components/ha-formfield";
|
||||
import type { HaSwitch } from "../../components/ha-switch";
|
||||
import {
|
||||
getConfigEntrySystemOptions,
|
||||
@ -82,13 +83,8 @@ class DialogConfigEntrySystemOptions extends LitElement {
|
||||
? html` <div class="error">${this._error}</div> `
|
||||
: ""}
|
||||
<div class="form">
|
||||
<ha-switch
|
||||
.checked=${!this._disableNewEntities}
|
||||
@change=${this._disableNewEntitiesChanged}
|
||||
.disabled=${this._submitting}
|
||||
>
|
||||
<div>
|
||||
<p>
|
||||
<ha-formfield
|
||||
.label=${html`<p>
|
||||
${this.hass.localize(
|
||||
"ui.dialogs.config_entry_system_options.enable_new_entities_label"
|
||||
)}
|
||||
@ -101,9 +97,15 @@ class DialogConfigEntrySystemOptions extends LitElement {
|
||||
`component.${this._params.entry.domain}.title`
|
||||
) || this._params.entry.domain
|
||||
)}
|
||||
</p>
|
||||
</div>
|
||||
</ha-switch>
|
||||
</p>`}
|
||||
>
|
||||
<ha-switch
|
||||
.checked=${!this._disableNewEntities}
|
||||
@change=${this._disableNewEntitiesChanged}
|
||||
.disabled=${this._submitting}
|
||||
>
|
||||
</ha-switch>
|
||||
</ha-formfield>
|
||||
</div>
|
||||
`}
|
||||
</paper-dialog-scrollable>
|
||||
@ -172,9 +174,6 @@ class DialogConfigEntrySystemOptions extends LitElement {
|
||||
padding-bottom: 24px;
|
||||
color: var(--primary-text-color);
|
||||
}
|
||||
p {
|
||||
margin: 0;
|
||||
}
|
||||
.secondary {
|
||||
color: var(--secondary-text-color);
|
||||
}
|
||||
|
@ -14,8 +14,7 @@ import {
|
||||
PropertyValues,
|
||||
TemplateResult,
|
||||
} from "lit-element";
|
||||
import "../../components/dialog/ha-paper-dialog";
|
||||
import type { HaPaperDialog } from "../../components/dialog/ha-paper-dialog";
|
||||
import "../../components/ha-dialog";
|
||||
import "../../components/ha-form/ha-form";
|
||||
import "../../components/ha-markdown";
|
||||
import {
|
||||
@ -27,7 +26,6 @@ import {
|
||||
DeviceRegistryEntry,
|
||||
subscribeDeviceRegistry,
|
||||
} from "../../data/device_registry";
|
||||
import { PolymerChangedEvent } from "../../polymer-types";
|
||||
import { haStyleDialog } from "../../resources/styles";
|
||||
import type { HomeAssistant } from "../../types";
|
||||
import { DataEntryFlowDialogParams } from "./show-dialog-data-entry-flow";
|
||||
@ -90,7 +88,6 @@ class DataEntryFlowDialog extends LitElement {
|
||||
// We only load the handlers once
|
||||
if (this._handlers === undefined) {
|
||||
this._loading = true;
|
||||
this.updateComplete.then(() => this._scheduleCenterDialog());
|
||||
try {
|
||||
this._handlers = await params.flowConfig.getFlowHandlers(this.hass);
|
||||
} finally {
|
||||
@ -98,7 +95,6 @@ class DataEntryFlowDialog extends LitElement {
|
||||
}
|
||||
}
|
||||
await this.updateComplete;
|
||||
this._scheduleCenterDialog();
|
||||
return;
|
||||
}
|
||||
|
||||
@ -115,9 +111,6 @@ class DataEntryFlowDialog extends LitElement {
|
||||
|
||||
this._processStep(step);
|
||||
this._loading = false;
|
||||
// When the flow changes, center the dialog.
|
||||
// Don't do it on each step or else the dialog keeps bouncing.
|
||||
this._scheduleCenterDialog();
|
||||
}
|
||||
|
||||
protected render(): TemplateResult {
|
||||
@ -126,80 +119,84 @@ class DataEntryFlowDialog extends LitElement {
|
||||
}
|
||||
|
||||
return html`
|
||||
<ha-paper-dialog
|
||||
with-backdrop
|
||||
opened
|
||||
modal
|
||||
@opened-changed=${this._openedChanged}
|
||||
<ha-dialog
|
||||
open
|
||||
@closing=${this._close}
|
||||
scrimClickAction
|
||||
escapeKeyAction
|
||||
hideActions
|
||||
>
|
||||
${this._loading || (this._step === null && this._handlers === undefined)
|
||||
? html`
|
||||
<step-flow-loading
|
||||
.label=${this.hass.localize(
|
||||
"ui.panel.config.integrations.config_flow.loading_first_time"
|
||||
)}
|
||||
></step-flow-loading>
|
||||
`
|
||||
: this._step === undefined
|
||||
? // When we are going to next step, we render 1 round of empty
|
||||
// to reset the element.
|
||||
""
|
||||
: html`
|
||||
<ha-icon-button
|
||||
aria-label=${this.hass.localize(
|
||||
"ui.panel.config.integrations.config_flow.dismiss"
|
||||
)}
|
||||
icon="hass:close"
|
||||
dialog-dismiss
|
||||
></ha-icon-button>
|
||||
${this._step === null
|
||||
? // Show handler picker
|
||||
html`
|
||||
<step-flow-pick-handler
|
||||
.flowConfig=${this._params.flowConfig}
|
||||
.hass=${this.hass}
|
||||
.handlers=${this._handlers}
|
||||
.showAdvanced=${this._params.showAdvanced}
|
||||
></step-flow-pick-handler>
|
||||
`
|
||||
: this._step.type === "form"
|
||||
? html`
|
||||
<step-flow-form
|
||||
.flowConfig=${this._params.flowConfig}
|
||||
.step=${this._step}
|
||||
.hass=${this.hass}
|
||||
></step-flow-form>
|
||||
`
|
||||
: this._step.type === "external"
|
||||
? html`
|
||||
<step-flow-external
|
||||
.flowConfig=${this._params.flowConfig}
|
||||
.step=${this._step}
|
||||
.hass=${this.hass}
|
||||
></step-flow-external>
|
||||
`
|
||||
: this._step.type === "abort"
|
||||
? html`
|
||||
<step-flow-abort
|
||||
.flowConfig=${this._params.flowConfig}
|
||||
.step=${this._step}
|
||||
.hass=${this.hass}
|
||||
></step-flow-abort>
|
||||
`
|
||||
: this._devices === undefined || this._areas === undefined
|
||||
? // When it's a create entry result, we will fetch device & area registry
|
||||
html` <step-flow-loading></step-flow-loading> `
|
||||
: html`
|
||||
<step-flow-create-entry
|
||||
.flowConfig=${this._params.flowConfig}
|
||||
.step=${this._step}
|
||||
.hass=${this.hass}
|
||||
.devices=${this._devices}
|
||||
.areas=${this._areas}
|
||||
></step-flow-create-entry>
|
||||
`}
|
||||
`}
|
||||
</ha-paper-dialog>
|
||||
<div>
|
||||
${this._loading ||
|
||||
(this._step === null && this._handlers === undefined)
|
||||
? html`
|
||||
<step-flow-loading
|
||||
.label=${this.hass.localize(
|
||||
"ui.panel.config.integrations.config_flow.loading_first_time"
|
||||
)}
|
||||
></step-flow-loading>
|
||||
`
|
||||
: this._step === undefined
|
||||
? // When we are going to next step, we render 1 round of empty
|
||||
// to reset the element.
|
||||
""
|
||||
: html`
|
||||
<ha-icon-button
|
||||
aria-label=${this.hass.localize(
|
||||
"ui.panel.config.integrations.config_flow.dismiss"
|
||||
)}
|
||||
icon="hass:close"
|
||||
dialogAction="close"
|
||||
></ha-icon-button>
|
||||
${this._step === null
|
||||
? // Show handler picker
|
||||
html`
|
||||
<step-flow-pick-handler
|
||||
.flowConfig=${this._params.flowConfig}
|
||||
.hass=${this.hass}
|
||||
.handlers=${this._handlers}
|
||||
.showAdvanced=${this._params.showAdvanced}
|
||||
></step-flow-pick-handler>
|
||||
`
|
||||
: this._step.type === "form"
|
||||
? html`
|
||||
<step-flow-form
|
||||
.flowConfig=${this._params.flowConfig}
|
||||
.step=${this._step}
|
||||
.hass=${this.hass}
|
||||
></step-flow-form>
|
||||
`
|
||||
: this._step.type === "external"
|
||||
? html`
|
||||
<step-flow-external
|
||||
.flowConfig=${this._params.flowConfig}
|
||||
.step=${this._step}
|
||||
.hass=${this.hass}
|
||||
></step-flow-external>
|
||||
`
|
||||
: this._step.type === "abort"
|
||||
? html`
|
||||
<step-flow-abort
|
||||
.flowConfig=${this._params.flowConfig}
|
||||
.step=${this._step}
|
||||
.hass=${this.hass}
|
||||
></step-flow-abort>
|
||||
`
|
||||
: this._devices === undefined || this._areas === undefined
|
||||
? // When it's a create entry result, we will fetch device & area registry
|
||||
html` <step-flow-loading></step-flow-loading> `
|
||||
: html`
|
||||
<step-flow-create-entry
|
||||
.flowConfig=${this._params.flowConfig}
|
||||
.step=${this._step}
|
||||
.hass=${this.hass}
|
||||
.devices=${this._devices}
|
||||
.areas=${this._areas}
|
||||
></step-flow-create-entry>
|
||||
`}
|
||||
`}
|
||||
</div>
|
||||
</ha-dialog>
|
||||
`;
|
||||
}
|
||||
|
||||
@ -225,18 +222,6 @@ class DataEntryFlowDialog extends LitElement {
|
||||
this._areas = [];
|
||||
}
|
||||
}
|
||||
|
||||
if (changedProps.has("_devices") && this._dialog) {
|
||||
this._scheduleCenterDialog();
|
||||
}
|
||||
}
|
||||
|
||||
private _scheduleCenterDialog() {
|
||||
setTimeout(() => this._dialog.center(), 0);
|
||||
}
|
||||
|
||||
private get _dialog(): HaPaperDialog {
|
||||
return this.shadowRoot!.querySelector("ha-paper-dialog")!;
|
||||
}
|
||||
|
||||
private async _fetchDevices(configEntryId) {
|
||||
@ -310,16 +295,13 @@ class DataEntryFlowDialog extends LitElement {
|
||||
}
|
||||
}
|
||||
|
||||
private _openedChanged(ev: PolymerChangedEvent<boolean>): void {
|
||||
// Closed dialog by clicking on the overlay
|
||||
if (!ev.detail.value) {
|
||||
if (this._step) {
|
||||
this._flowDone();
|
||||
} else if (this._step === null) {
|
||||
// Flow aborted during picking flow
|
||||
this._step = undefined;
|
||||
this._params = undefined;
|
||||
}
|
||||
private _close(): void {
|
||||
if (this._step) {
|
||||
this._flowDone();
|
||||
} else if (this._step === null) {
|
||||
// Flow aborted during picking flow
|
||||
this._step = undefined;
|
||||
this._params = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
@ -327,18 +309,14 @@ class DataEntryFlowDialog extends LitElement {
|
||||
return [
|
||||
haStyleDialog,
|
||||
css`
|
||||
ha-paper-dialog {
|
||||
max-width: 600px;
|
||||
}
|
||||
ha-paper-dialog > * {
|
||||
margin: 0;
|
||||
display: block;
|
||||
padding: 0;
|
||||
ha-dialog {
|
||||
--dialog-content-padding: 0;
|
||||
}
|
||||
ha-icon-button {
|
||||
display: inline-block;
|
||||
padding: 8px;
|
||||
float: right;
|
||||
padding: 16px;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
}
|
||||
`,
|
||||
];
|
||||
|
@ -2,8 +2,21 @@ import { css } from "lit-element";
|
||||
|
||||
export const configFlowContentStyles = css`
|
||||
h2 {
|
||||
margin-top: 24px;
|
||||
margin: 24px 0 0;
|
||||
padding: 0 24px;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
font-family: var(
|
||||
--mdc-typography-headline6-font-family,
|
||||
var(--mdc-typography-font-family, Roboto, sans-serif)
|
||||
);
|
||||
font-size: var(--mdc-typography-headline6-font-size, 1.25rem);
|
||||
line-height: var(--mdc-typography-headline6-line-height, 2rem);
|
||||
font-weight: var(--mdc-typography-headline6-font-weight, 500);
|
||||
letter-spacing: var(--mdc-typography-headline6-letter-spacing, 0.0125em);
|
||||
text-decoration: var(--mdc-typography-headline6-text-decoration, inherit);
|
||||
text-transform: var(--mdc-typography-headline6-text-transform, inherit);
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.content {
|
||||
|
@ -1,5 +1,4 @@
|
||||
import "@material/mwc-button/mwc-button";
|
||||
import "@polymer/paper-dialog-scrollable/paper-dialog-scrollable";
|
||||
import "@polymer/paper-input/paper-input";
|
||||
import {
|
||||
css,
|
||||
@ -11,7 +10,7 @@ import {
|
||||
TemplateResult,
|
||||
} from "lit-element";
|
||||
import { classMap } from "lit-html/directives/class-map";
|
||||
import "../../components/dialog/ha-paper-dialog";
|
||||
import "../../components/ha-dialog";
|
||||
import "../../components/ha-switch";
|
||||
import { PolymerChangedEvent } from "../../polymer-types";
|
||||
import { haStyleDialog } from "../../resources/styles";
|
||||
@ -41,21 +40,17 @@ class DialogBox extends LitElement {
|
||||
const confirmPrompt = this._params.confirmation || this._params.prompt;
|
||||
|
||||
return html`
|
||||
<ha-paper-dialog
|
||||
with-backdrop
|
||||
opened
|
||||
modal
|
||||
@opened-changed="${this._openedChanged}"
|
||||
<ha-dialog
|
||||
open
|
||||
scrimClickAction
|
||||
escapeKeyAction
|
||||
@close=${this._close}
|
||||
.heading=${this._params.title
|
||||
? this._params.title
|
||||
: this._params.confirmation &&
|
||||
this.hass.localize("ui.dialogs.generic.default_confirmation_title")}
|
||||
>
|
||||
<h2>
|
||||
${this._params.title
|
||||
? this._params.title
|
||||
: this._params.confirmation &&
|
||||
this.hass.localize(
|
||||
"ui.dialogs.generic.default_confirmation_title"
|
||||
)}
|
||||
</h2>
|
||||
<paper-dialog-scrollable>
|
||||
<div>
|
||||
${this._params.text
|
||||
? html`
|
||||
<p
|
||||
@ -83,23 +78,21 @@ class DialogBox extends LitElement {
|
||||
></paper-input>
|
||||
`
|
||||
: ""}
|
||||
</paper-dialog-scrollable>
|
||||
<div class="paper-dialog-buttons">
|
||||
${confirmPrompt &&
|
||||
html`
|
||||
<mwc-button @click="${this._dismiss}">
|
||||
${this._params.dismissText
|
||||
? this._params.dismissText
|
||||
: this.hass.localize("ui.dialogs.generic.cancel")}
|
||||
</mwc-button>
|
||||
`}
|
||||
<mwc-button @click="${this._confirm}">
|
||||
${this._params.confirmText
|
||||
? this._params.confirmText
|
||||
: this.hass.localize("ui.dialogs.generic.ok")}
|
||||
</mwc-button>
|
||||
</div>
|
||||
</ha-paper-dialog>
|
||||
${confirmPrompt &&
|
||||
html`
|
||||
<mwc-button @click=${this._dismiss} slot="secondaryAction">
|
||||
${this._params.dismissText
|
||||
? this._params.dismissText
|
||||
: this.hass.localize("ui.dialogs.generic.cancel")}
|
||||
</mwc-button>
|
||||
`}
|
||||
<mwc-button @click=${this._confirm} slot="primaryAction">
|
||||
${this._params.confirmText
|
||||
? this._params.confirmText
|
||||
: this.hass.localize("ui.dialogs.generic.ok")}
|
||||
</mwc-button>
|
||||
</ha-dialog>
|
||||
`;
|
||||
}
|
||||
|
||||
@ -127,10 +120,8 @@ class DialogBox extends LitElement {
|
||||
this._dismiss();
|
||||
}
|
||||
|
||||
private _openedChanged(ev: PolymerChangedEvent<boolean>): void {
|
||||
if (!(ev.detail as any).value) {
|
||||
this._params = undefined;
|
||||
}
|
||||
private _close(): void {
|
||||
this._params = undefined;
|
||||
}
|
||||
|
||||
static get styles(): CSSResult[] {
|
||||
@ -141,15 +132,6 @@ class DialogBox extends LitElement {
|
||||
pointer-events: initial !important;
|
||||
cursor: initial !important;
|
||||
}
|
||||
ha-paper-dialog {
|
||||
min-width: 400px;
|
||||
max-width: 500px;
|
||||
}
|
||||
@media (max-width: 400px) {
|
||||
ha-paper-dialog {
|
||||
min-width: initial;
|
||||
}
|
||||
}
|
||||
a {
|
||||
color: var(--primary-color);
|
||||
}
|
||||
@ -165,6 +147,10 @@ class DialogBox extends LitElement {
|
||||
.secondary {
|
||||
color: var(--secondary-text-color);
|
||||
}
|
||||
ha-dialog {
|
||||
/* Place above other dialogs */
|
||||
--dialog-z-index: 104;
|
||||
}
|
||||
`,
|
||||
];
|
||||
}
|
||||
|
@ -11,6 +11,7 @@ import {
|
||||
import { computeStateName } from "../../common/entity/compute_state_name";
|
||||
import "../../components/ha-dialog";
|
||||
import "../../components/ha-switch";
|
||||
import "../../components/ha-formfield";
|
||||
import type { HaSwitch } from "../../components/ha-switch";
|
||||
import { computeDeviceName } from "../../data/device_registry";
|
||||
import { fetchMQTTDebugInfo, MQTTDeviceDebugInfo } from "../../data/mqtt";
|
||||
@ -61,22 +62,28 @@ class DialogMQTTDeviceDebugInfo extends LitElement {
|
||||
"ui.dialogs.mqtt_device_debug_info.payload_display"
|
||||
)}
|
||||
</h4>
|
||||
<ha-switch
|
||||
.checked=${this._showDeserialized}
|
||||
@change=${this._showDeserializedChanged}
|
||||
>
|
||||
${this.hass!.localize(
|
||||
<ha-formfield
|
||||
.label=${this.hass!.localize(
|
||||
"ui.dialogs.mqtt_device_debug_info.deserialize"
|
||||
)}
|
||||
</ha-switch>
|
||||
<ha-switch
|
||||
.checked=${this._showAsYaml}
|
||||
@change=${this._showAsYamlChanged}
|
||||
>
|
||||
${this.hass!.localize(
|
||||
<ha-switch
|
||||
.checked=${this._showDeserialized}
|
||||
@change=${this._showDeserializedChanged}
|
||||
>
|
||||
</ha-switch>
|
||||
</ha-formfield>
|
||||
<ha-formfield
|
||||
.label=${this.hass!.localize(
|
||||
"ui.dialogs.mqtt_device_debug_info.show_as_yaml"
|
||||
)}
|
||||
</ha-switch>
|
||||
>
|
||||
<ha-switch
|
||||
.checked=${this._showAsYaml}
|
||||
@change=${this._showAsYamlChanged}
|
||||
>
|
||||
</ha-switch>
|
||||
</ha-formfield>
|
||||
<h4>
|
||||
${this.hass!.localize("ui.dialogs.mqtt_device_debug_info.entities")}
|
||||
</h4>
|
||||
|
@ -1,113 +0,0 @@
|
||||
import "@polymer/paper-dialog-scrollable/paper-dialog-scrollable";
|
||||
import {
|
||||
css,
|
||||
CSSResult,
|
||||
customElement,
|
||||
html,
|
||||
LitElement,
|
||||
property,
|
||||
TemplateResult,
|
||||
} from "lit-element";
|
||||
import "../../components/dialog/ha-paper-dialog";
|
||||
import type { HaPaperDialog } from "../../components/dialog/ha-paper-dialog";
|
||||
import { fetchZHADevice, ZHADevice } from "../../data/zha";
|
||||
import "../../panels/config/zha/zha-device-card";
|
||||
import type { PolymerChangedEvent } from "../../polymer-types";
|
||||
import { haStyleDialog } from "../../resources/styles";
|
||||
import type { HomeAssistant } from "../../types";
|
||||
import type { ZHADeviceInfoDialogParams } from "./show-dialog-zha-device-info";
|
||||
|
||||
@customElement("dialog-zha-device-info")
|
||||
class DialogZHADeviceInfo extends LitElement {
|
||||
@property() public hass!: HomeAssistant;
|
||||
|
||||
@property() private _params?: ZHADeviceInfoDialogParams;
|
||||
|
||||
@property() private _error?: string;
|
||||
|
||||
@property() private _device?: ZHADevice;
|
||||
|
||||
public async showDialog(params: ZHADeviceInfoDialogParams): Promise<void> {
|
||||
this._params = params;
|
||||
this._device = await fetchZHADevice(this.hass, params.ieee);
|
||||
await this.updateComplete;
|
||||
this._dialog.open();
|
||||
}
|
||||
|
||||
protected render(): TemplateResult {
|
||||
if (!this._params || !this._device) {
|
||||
return html``;
|
||||
}
|
||||
|
||||
return html`
|
||||
<ha-paper-dialog
|
||||
with-backdrop
|
||||
opened
|
||||
@opened-changed=${this._openedChanged}
|
||||
>
|
||||
${this._error
|
||||
? html` <div class="error">${this._error}</div> `
|
||||
: html`
|
||||
<zha-device-card
|
||||
class="card"
|
||||
.hass=${this.hass}
|
||||
.device=${this._device}
|
||||
@zha-device-removed=${this._onDeviceRemoved}
|
||||
.showEntityDetail=${false}
|
||||
.showActions="${this._device.device_type !== "Coordinator"}"
|
||||
></zha-device-card>
|
||||
`}
|
||||
</ha-paper-dialog>
|
||||
`;
|
||||
}
|
||||
|
||||
private _openedChanged(ev: PolymerChangedEvent<boolean>): void {
|
||||
if (!ev.detail.value) {
|
||||
this._params = undefined;
|
||||
this._error = undefined;
|
||||
this._device = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
private _onDeviceRemoved(): void {
|
||||
this._closeDialog();
|
||||
}
|
||||
|
||||
private get _dialog(): HaPaperDialog {
|
||||
return this.shadowRoot!.querySelector("ha-paper-dialog")!;
|
||||
}
|
||||
|
||||
private _closeDialog() {
|
||||
this._dialog.close();
|
||||
}
|
||||
|
||||
static get styles(): CSSResult[] {
|
||||
return [
|
||||
haStyleDialog,
|
||||
css`
|
||||
ha-paper-dialog > * {
|
||||
margin: 0;
|
||||
display: block;
|
||||
padding: 0;
|
||||
}
|
||||
.card {
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
flex: 1 0 300px;
|
||||
min-width: 0;
|
||||
max-width: 600px;
|
||||
word-wrap: break-word;
|
||||
}
|
||||
.error {
|
||||
color: var(--google-red-500);
|
||||
}
|
||||
`,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"dialog-zha-device-info": DialogZHADeviceInfo;
|
||||
}
|
||||
}
|
@ -1,21 +0,0 @@
|
||||
import { fireEvent } from "../../common/dom/fire_event";
|
||||
|
||||
export interface ZHADeviceInfoDialogParams {
|
||||
ieee: string;
|
||||
}
|
||||
|
||||
export const loadZHADeviceInfoDialog = () =>
|
||||
import(
|
||||
/* webpackChunkName: "dialog-zha-device-info" */ "./dialog-zha-device-info"
|
||||
);
|
||||
|
||||
export const showZHADeviceInfoDialog = (
|
||||
element: HTMLElement,
|
||||
zhaDeviceInfoParams: ZHADeviceInfoDialogParams
|
||||
): void => {
|
||||
fireEvent(element, "show-dialog", {
|
||||
dialogTag: "dialog-zha-device-info",
|
||||
dialogImport: loadZHADeviceInfoDialog,
|
||||
dialogParams: zhaDeviceInfoParams,
|
||||
});
|
||||
};
|
@ -47,7 +47,6 @@
|
||||
(function() {
|
||||
// Safari 10.1 supports type=module but ignores nomodule, so we add this check.
|
||||
if (!isS101) {
|
||||
_ls("/static/polyfills/custom-elements-es5-adapter.js");
|
||||
<% if (useRollup) { %>
|
||||
_ls("/static/js/s.min.js").onload = function() {
|
||||
System.import("<%= es5PageJS %>");
|
||||
|
@ -66,7 +66,6 @@
|
||||
(function() {
|
||||
if (!window.latestJS) {
|
||||
window.customPanelJS = "<%= es5CustomPanelJS %>";
|
||||
_ls("/static/polyfills/custom-elements-es5-adapter.js");
|
||||
|
||||
<% if (useRollup) { %>
|
||||
_ls("/static/js/s.min.js").onload = function() {
|
||||
|
@ -49,7 +49,6 @@
|
||||
(function() {
|
||||
// Safari 10.1 supports type=module but ignores nomodule, so we add this check.
|
||||
if (!isS101) {
|
||||
_ls("/static/polyfills/custom-elements-es5-adapter.js");
|
||||
<% if (useRollup) { %>
|
||||
_ls("/static/js/s.min.js").onload = function() {
|
||||
System.import("<%= es5PageJS %>");
|
||||
|
@ -91,6 +91,20 @@ class PartialPanelResolver extends HassRouterPage {
|
||||
|
||||
private _waitForStart = false;
|
||||
|
||||
private _disconnectedPanel?: ChildNode;
|
||||
|
||||
private _hiddenTimeout?: number;
|
||||
|
||||
protected firstUpdated(changedProps: PropertyValues) {
|
||||
super.firstUpdated(changedProps);
|
||||
|
||||
document.addEventListener(
|
||||
"visibilitychange",
|
||||
() => this._handleVisibilityChange(),
|
||||
false
|
||||
);
|
||||
}
|
||||
|
||||
protected updated(changedProps: PropertyValues) {
|
||||
super.updated(changedProps);
|
||||
|
||||
@ -141,6 +155,27 @@ class PartialPanelResolver extends HassRouterPage {
|
||||
}
|
||||
}
|
||||
|
||||
private _handleVisibilityChange() {
|
||||
if (document.hidden) {
|
||||
this._hiddenTimeout = window.setTimeout(() => {
|
||||
this._hiddenTimeout = undefined;
|
||||
if (this.lastChild) {
|
||||
this._disconnectedPanel = this.lastChild;
|
||||
this.removeChild(this.lastChild);
|
||||
}
|
||||
}, 300000);
|
||||
} else {
|
||||
if (this._hiddenTimeout) {
|
||||
clearTimeout(this._hiddenTimeout);
|
||||
this._hiddenTimeout = undefined;
|
||||
}
|
||||
if (this._disconnectedPanel) {
|
||||
this.appendChild(this._disconnectedPanel);
|
||||
this._disconnectedPanel = undefined;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async _updateRoutes(oldPanels?: HomeAssistant["panels"]) {
|
||||
this.routerOptions = getRoutes(this.hass.panels);
|
||||
|
||||
|
@ -1,9 +1,12 @@
|
||||
import "@polymer/paper-dropdown-menu/paper-dropdown-menu-light";
|
||||
import "../../../../components/ha-icon-button";
|
||||
import "@material/mwc-list/mwc-list-item";
|
||||
import "@material/mwc-icon-button";
|
||||
import "../../../../components/ha-button-menu";
|
||||
import "../../../../components/ha-svg-icon";
|
||||
import { mdiDotsVertical, mdiArrowUp, mdiArrowDown } from "@mdi/js";
|
||||
import "@polymer/paper-item/paper-item";
|
||||
import "@polymer/paper-listbox/paper-listbox";
|
||||
import type { PaperListboxElement } from "@polymer/paper-listbox/paper-listbox";
|
||||
import "@polymer/paper-menu-button/paper-menu-button";
|
||||
import {
|
||||
css,
|
||||
CSSResult,
|
||||
@ -96,56 +99,64 @@ export default class HaAutomationActionRow extends LitElement {
|
||||
<div class="card-menu">
|
||||
${this.index !== 0
|
||||
? html`
|
||||
<ha-icon-button
|
||||
icon="hass:arrow-up"
|
||||
<mwc-icon-button
|
||||
.title=${this.hass.localize(
|
||||
"ui.panel.config.automation.editor.move_up"
|
||||
)}
|
||||
.label=${this.hass.localize(
|
||||
"ui.panel.config.automation.editor.move_up"
|
||||
)}
|
||||
@click=${this._moveUp}
|
||||
></ha-icon-button>
|
||||
>
|
||||
<ha-svg-icon path=${mdiArrowUp}></ha-svg-icon>
|
||||
</mwc-icon-button>
|
||||
`
|
||||
: ""}
|
||||
${this.index !== this.totalActions - 1
|
||||
? html`
|
||||
<ha-icon-button
|
||||
icon="hass:arrow-down"
|
||||
<mwc-icon-button
|
||||
.title=${this.hass.localize(
|
||||
"ui.panel.config.automation.editor.move_down"
|
||||
)}
|
||||
.label=${this.hass.localize(
|
||||
"ui.panel.config.automation.editor.move_down"
|
||||
)}
|
||||
@click=${this._moveDown}
|
||||
></ha-icon-button>
|
||||
>
|
||||
<ha-svg-icon path=${mdiArrowDown}></ha-svg-icon>
|
||||
</mwc-icon-button>
|
||||
`
|
||||
: ""}
|
||||
<paper-menu-button
|
||||
no-animations
|
||||
horizontal-align="right"
|
||||
horizontal-offset="-5"
|
||||
vertical-offset="-5"
|
||||
close-on-activate
|
||||
>
|
||||
<ha-icon-button
|
||||
icon="hass:dots-vertical"
|
||||
slot="dropdown-trigger"
|
||||
></ha-icon-button>
|
||||
<paper-listbox slot="dropdown-content">
|
||||
<paper-item
|
||||
@tap=${this._switchYamlMode}
|
||||
.disabled=${selected === -1}
|
||||
>
|
||||
${yamlMode
|
||||
? this.hass.localize(
|
||||
"ui.panel.config.automation.editor.edit_ui"
|
||||
)
|
||||
: this.hass.localize(
|
||||
"ui.panel.config.automation.editor.edit_yaml"
|
||||
)}
|
||||
</paper-item>
|
||||
<paper-item disabled>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.automation.editor.actions.duplicate"
|
||||
)}
|
||||
</paper-item>
|
||||
<paper-item @tap=${this._onDelete}>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.automation.editor.actions.delete"
|
||||
)}
|
||||
</paper-item>
|
||||
</paper-listbox>
|
||||
</paper-menu-button>
|
||||
<ha-button-menu corner="BOTTOM_START">
|
||||
<mwc-icon-button
|
||||
slot="trigger"
|
||||
.title=${this.hass.localize("ui.common.menu")}
|
||||
.label=${this.hass.localize("ui.common.overflow_menu")}
|
||||
><ha-svg-icon path=${mdiDotsVertical}></ha-svg-icon>
|
||||
</mwc-icon-button>
|
||||
<mwc-list-item
|
||||
@tap=${this._switchYamlMode}
|
||||
.disabled=${selected === -1}
|
||||
>
|
||||
${yamlMode
|
||||
? this.hass.localize(
|
||||
"ui.panel.config.automation.editor.edit_ui"
|
||||
)
|
||||
: this.hass.localize(
|
||||
"ui.panel.config.automation.editor.edit_yaml"
|
||||
)}
|
||||
</mwc-list-item>
|
||||
<mwc-list-item disabled>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.automation.editor.actions.duplicate"
|
||||
)}
|
||||
</mwc-list-item>
|
||||
<mwc-list-item @tap=${this._onDelete}>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.automation.editor.actions.delete"
|
||||
)}
|
||||
</mwc-list-item>
|
||||
</ha-button-menu>
|
||||
</div>
|
||||
${yamlMode
|
||||
? html`
|
||||
@ -259,14 +270,17 @@ export default class HaAutomationActionRow extends LitElement {
|
||||
top: 0;
|
||||
right: 0;
|
||||
z-index: 3;
|
||||
color: var(--primary-text-color);
|
||||
--mdc-theme-text-primary-on-background: var(--primary-text-color);
|
||||
}
|
||||
.rtl .card-menu {
|
||||
right: auto;
|
||||
left: 0;
|
||||
}
|
||||
.card-menu paper-item {
|
||||
cursor: pointer;
|
||||
ha-button-menu {
|
||||
margin: 8px;
|
||||
}
|
||||
mwc-list-item[disabled] {
|
||||
--mdc-theme-text-primary-on-background: var(--disabled-text-color);
|
||||
}
|
||||
`;
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
import "@polymer/paper-input/paper-input";
|
||||
import "@polymer/paper-input/paper-textarea";
|
||||
import { customElement, LitElement, property } from "lit-element";
|
||||
import { html } from "lit-html";
|
||||
import { WaitAction } from "../../../../../data/script";
|
||||
@ -19,7 +20,7 @@ export class HaWaitAction extends LitElement implements ActionElement {
|
||||
const { wait_template, timeout } = this.action;
|
||||
|
||||
return html`
|
||||
<ha-textarea
|
||||
<paper-textarea
|
||||
.label=${this.hass.localize(
|
||||
"ui.panel.config.automation.editor.actions.type.wait_template.wait_template"
|
||||
)}
|
||||
@ -27,7 +28,7 @@ export class HaWaitAction extends LitElement implements ActionElement {
|
||||
.value=${wait_template}
|
||||
@value-changed=${this._valueChanged}
|
||||
dir="ltr"
|
||||
></ha-textarea>
|
||||
></paper-textarea>
|
||||
<paper-input
|
||||
.label=${this.hass.localize(
|
||||
"ui.panel.config.automation.editor.actions.type.wait_template.timeout"
|
||||
|
@ -1,6 +1,8 @@
|
||||
import "../../../../components/ha-icon-button";
|
||||
import "@polymer/paper-item/paper-item";
|
||||
import "@polymer/paper-menu-button/paper-menu-button";
|
||||
import "@material/mwc-list/mwc-list-item";
|
||||
import "../../../../components/ha-button-menu";
|
||||
import { mdiDotsVertical } from "@mdi/js";
|
||||
import {
|
||||
css,
|
||||
CSSResult,
|
||||
@ -61,39 +63,33 @@ export default class HaAutomationConditionRow extends LitElement {
|
||||
<ha-card>
|
||||
<div class="card-content">
|
||||
<div class="card-menu">
|
||||
<paper-menu-button
|
||||
no-animations
|
||||
horizontal-align="right"
|
||||
horizontal-offset="-5"
|
||||
vertical-offset="-5"
|
||||
close-on-activate
|
||||
>
|
||||
<ha-icon-button
|
||||
icon="hass:dots-vertical"
|
||||
slot="dropdown-trigger"
|
||||
></ha-icon-button>
|
||||
<paper-listbox slot="dropdown-content">
|
||||
<paper-item @tap=${this._switchYamlMode}>
|
||||
${this._yamlMode
|
||||
? this.hass.localize(
|
||||
"ui.panel.config.automation.editor.edit_ui"
|
||||
)
|
||||
: this.hass.localize(
|
||||
"ui.panel.config.automation.editor.edit_yaml"
|
||||
)}
|
||||
</paper-item>
|
||||
<paper-item disabled>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.automation.editor.conditions.duplicate"
|
||||
)}
|
||||
</paper-item>
|
||||
<paper-item @tap=${this._onDelete}>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.automation.editor.conditions.delete"
|
||||
)}
|
||||
</paper-item>
|
||||
</paper-listbox>
|
||||
</paper-menu-button>
|
||||
<ha-button-menu corner="BOTTOM_START">
|
||||
<mwc-icon-button
|
||||
.title=${this.hass.localize("ui.common.menu")}
|
||||
.label=${this.hass.localize("ui.common.overflow_menu")}
|
||||
slot="trigger"
|
||||
><ha-svg-icon path=${mdiDotsVertical}></ha-svg-icon
|
||||
></mwc-icon-button>
|
||||
<mwc-list-item @tap=${this._switchYamlMode}>
|
||||
${this._yamlMode
|
||||
? this.hass.localize(
|
||||
"ui.panel.config.automation.editor.edit_ui"
|
||||
)
|
||||
: this.hass.localize(
|
||||
"ui.panel.config.automation.editor.edit_yaml"
|
||||
)}
|
||||
</mwc-list-item>
|
||||
<mwc-list-item disabled>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.automation.editor.actions.duplicate"
|
||||
)}
|
||||
</mwc-list-item>
|
||||
<mwc-list-item @tap=${this._onDelete}>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.automation.editor.actions.delete"
|
||||
)}
|
||||
</mwc-list-item>
|
||||
</ha-button-menu>
|
||||
</div>
|
||||
<ha-automation-condition-editor
|
||||
.yamlMode=${this._yamlMode}
|
||||
@ -129,14 +125,17 @@ export default class HaAutomationConditionRow extends LitElement {
|
||||
top: 0;
|
||||
right: 0;
|
||||
z-index: 3;
|
||||
color: var(--primary-text-color);
|
||||
--mdc-theme-text-primary-on-background: var(--primary-text-color);
|
||||
}
|
||||
.rtl .card-menu {
|
||||
right: auto;
|
||||
left: 0;
|
||||
}
|
||||
.card-menu paper-item {
|
||||
cursor: pointer;
|
||||
ha-button-menu {
|
||||
margin: 8px;
|
||||
}
|
||||
mwc-list-item[disabled] {
|
||||
--mdc-theme-text-primary-on-background: var(--disabled-text-color);
|
||||
}
|
||||
`;
|
||||
}
|
||||
|
@ -2,7 +2,7 @@ import "@polymer/paper-input/paper-input";
|
||||
import { customElement, html, LitElement, property } from "lit-element";
|
||||
import { fireEvent } from "../../../../../common/dom/fire_event";
|
||||
import "../../../../../components/entity/ha-entity-picker";
|
||||
import "../../../../../components/ha-textarea";
|
||||
import "@polymer/paper-input/paper-textarea";
|
||||
import { NumericStateCondition } from "../../../../../data/automation";
|
||||
import { HomeAssistant } from "../../../../../types";
|
||||
import { handleChangeEvent } from "../ha-automation-condition-row";
|
||||
@ -45,7 +45,7 @@ export default class HaNumericStateCondition extends LitElement {
|
||||
.value=${below}
|
||||
@value-changed=${this._valueChanged}
|
||||
></paper-input>
|
||||
<ha-textarea
|
||||
<paper-textarea
|
||||
.label=${this.hass.localize(
|
||||
"ui.panel.config.automation.editor.conditions.type.numeric_state.value_template"
|
||||
)}
|
||||
@ -53,7 +53,7 @@ export default class HaNumericStateCondition extends LitElement {
|
||||
.value=${value_template}
|
||||
@value-changed=${this._valueChanged}
|
||||
dir="ltr"
|
||||
></ha-textarea>
|
||||
></paper-textarea>
|
||||
`;
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { customElement, html, LitElement, property } from "lit-element";
|
||||
import "../../../../../components/ha-textarea";
|
||||
import "@polymer/paper-input/paper-textarea";
|
||||
import { TemplateCondition } from "../../../../../data/automation";
|
||||
import { HomeAssistant } from "../../../../../types";
|
||||
import { handleChangeEvent } from "../ha-automation-condition-row";
|
||||
@ -17,7 +17,7 @@ export class HaTemplateCondition extends LitElement {
|
||||
protected render() {
|
||||
const { value_template } = this.condition;
|
||||
return html`
|
||||
<ha-textarea
|
||||
<paper-textarea
|
||||
.label=${this.hass.localize(
|
||||
"ui.panel.config.automation.editor.conditions.type.template.value_template"
|
||||
)}
|
||||
@ -25,7 +25,7 @@ export class HaTemplateCondition extends LitElement {
|
||||
.value=${value_template}
|
||||
@value-changed=${this._valueChanged}
|
||||
dir="ltr"
|
||||
></ha-textarea>
|
||||
></paper-textarea>
|
||||
`;
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,6 @@
|
||||
import "@polymer/app-layout/app-header/app-header";
|
||||
import "@polymer/app-layout/app-toolbar/app-toolbar";
|
||||
import "@polymer/paper-input/paper-textarea";
|
||||
import "../../../components/ha-icon-button";
|
||||
import {
|
||||
css,
|
||||
@ -117,7 +118,7 @@ export class HaAutomationEditor extends LitElement {
|
||||
@value-changed=${this._valueChanged}
|
||||
>
|
||||
</paper-input>
|
||||
<ha-textarea
|
||||
<paper-textarea
|
||||
.label=${this.hass.localize(
|
||||
"ui.panel.config.automation.editor.description.label"
|
||||
)}
|
||||
@ -127,7 +128,7 @@ export class HaAutomationEditor extends LitElement {
|
||||
name="description"
|
||||
.value=${this._config.description}
|
||||
@value-changed=${this._valueChanged}
|
||||
></ha-textarea>
|
||||
></paper-textarea>
|
||||
</div>
|
||||
${stateObj
|
||||
? html`
|
||||
|
@ -2,8 +2,10 @@ import "@polymer/paper-dropdown-menu/paper-dropdown-menu-light";
|
||||
import "../../../../components/ha-icon-button";
|
||||
import "@polymer/paper-item/paper-item";
|
||||
import "@polymer/paper-listbox/paper-listbox";
|
||||
import "@material/mwc-list/mwc-list-item";
|
||||
import "../../../../components/ha-button-menu";
|
||||
import { mdiDotsVertical } from "@mdi/js";
|
||||
import type { PaperListboxElement } from "@polymer/paper-listbox/paper-listbox";
|
||||
import "@polymer/paper-menu-button/paper-menu-button";
|
||||
import {
|
||||
css,
|
||||
CSSResult,
|
||||
@ -90,42 +92,36 @@ export default class HaAutomationTriggerRow extends LitElement {
|
||||
<ha-card>
|
||||
<div class="card-content">
|
||||
<div class="card-menu">
|
||||
<paper-menu-button
|
||||
no-animations
|
||||
horizontal-align="right"
|
||||
horizontal-offset="-5"
|
||||
vertical-offset="-5"
|
||||
close-on-activate
|
||||
>
|
||||
<ha-icon-button
|
||||
icon="hass:dots-vertical"
|
||||
slot="dropdown-trigger"
|
||||
></ha-icon-button>
|
||||
<paper-listbox slot="dropdown-content">
|
||||
<paper-item
|
||||
@tap=${this._switchYamlMode}
|
||||
.disabled=${selected === -1}
|
||||
>
|
||||
${yamlMode
|
||||
? this.hass.localize(
|
||||
"ui.panel.config.automation.editor.edit_ui"
|
||||
)
|
||||
: this.hass.localize(
|
||||
"ui.panel.config.automation.editor.edit_yaml"
|
||||
)}
|
||||
</paper-item>
|
||||
<paper-item disabled>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.automation.editor.triggers.duplicate"
|
||||
)}
|
||||
</paper-item>
|
||||
<paper-item @tap=${this._onDelete}>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.automation.editor.triggers.delete"
|
||||
)}
|
||||
</paper-item>
|
||||
</paper-listbox>
|
||||
</paper-menu-button>
|
||||
<ha-button-menu corner="BOTTOM_START">
|
||||
<mwc-icon-button
|
||||
slot="trigger"
|
||||
.title=${this.hass.localize("ui.common.menu")}
|
||||
.label=${this.hass.localize("ui.common.overflow_menu")}
|
||||
><ha-svg-icon path=${mdiDotsVertical}></ha-svg-icon
|
||||
></mwc-icon-button>
|
||||
<mwc-list-item
|
||||
@tap=${this._switchYamlMode}
|
||||
.disabled=${selected === -1}
|
||||
>
|
||||
${yamlMode
|
||||
? this.hass.localize(
|
||||
"ui.panel.config.automation.editor.edit_ui"
|
||||
)
|
||||
: this.hass.localize(
|
||||
"ui.panel.config.automation.editor.edit_yaml"
|
||||
)}
|
||||
</mwc-list-item>
|
||||
<mwc-list-item disabled>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.automation.editor.actions.duplicate"
|
||||
)}
|
||||
</mwc-list-item>
|
||||
<mwc-list-item @tap=${this._onDelete}>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.automation.editor.actions.delete"
|
||||
)}
|
||||
</mwc-list-item>
|
||||
</ha-button-menu>
|
||||
</div>
|
||||
${yamlMode
|
||||
? html`
|
||||
@ -232,14 +228,17 @@ export default class HaAutomationTriggerRow extends LitElement {
|
||||
top: 0;
|
||||
right: 0;
|
||||
z-index: 3;
|
||||
color: var(--primary-text-color);
|
||||
--mdc-theme-text-primary-on-background: var(--primary-text-color);
|
||||
}
|
||||
.rtl .card-menu {
|
||||
right: auto;
|
||||
left: 0;
|
||||
}
|
||||
.card-menu paper-item {
|
||||
cursor: pointer;
|
||||
ha-button-menu {
|
||||
margin: 8px;
|
||||
}
|
||||
mwc-list-item[disabled] {
|
||||
--mdc-theme-text-primary-on-background: var(--disabled-text-color);
|
||||
}
|
||||
`;
|
||||
}
|
||||
|
@ -2,7 +2,7 @@ import "@polymer/paper-input/paper-input";
|
||||
import { customElement, html, LitElement, property } from "lit-element";
|
||||
import { fireEvent } from "../../../../../common/dom/fire_event";
|
||||
import "../../../../../components/entity/ha-entity-picker";
|
||||
import "../../../../../components/ha-textarea";
|
||||
import "@polymer/paper-input/paper-textarea";
|
||||
import { ForDict, NumericStateTrigger } from "../../../../../data/automation";
|
||||
import { HomeAssistant } from "../../../../../types";
|
||||
import { handleChangeEvent } from "../ha-automation-trigger-row";
|
||||
@ -61,7 +61,7 @@ export default class HaNumericStateTrigger extends LitElement {
|
||||
.value=${below}
|
||||
@value-changed=${this._valueChanged}
|
||||
></paper-input>
|
||||
<ha-textarea
|
||||
<paper-textarea
|
||||
.label=${this.hass.localize(
|
||||
"ui.panel.config.automation.editor.triggers.type.numeric_state.value_template"
|
||||
)}
|
||||
@ -69,7 +69,7 @@ export default class HaNumericStateTrigger extends LitElement {
|
||||
.value=${value_template}
|
||||
@value-changed=${this._valueChanged}
|
||||
dir="ltr"
|
||||
></ha-textarea>
|
||||
></paper-textarea>
|
||||
<paper-input
|
||||
.label=${this.hass.localize(
|
||||
"ui.panel.config.automation.editor.triggers.type.state.for"
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { customElement, html, LitElement, property } from "lit-element";
|
||||
import "../../../../../components/ha-textarea";
|
||||
import "@polymer/paper-input/paper-textarea";
|
||||
import { TemplateTrigger } from "../../../../../data/automation";
|
||||
import { HomeAssistant } from "../../../../../types";
|
||||
import { handleChangeEvent } from "../ha-automation-trigger-row";
|
||||
@ -17,7 +17,7 @@ export class HaTemplateTrigger extends LitElement {
|
||||
protected render() {
|
||||
const { value_template } = this.trigger;
|
||||
return html`
|
||||
<ha-textarea
|
||||
<paper-textarea
|
||||
.label=${this.hass.localize(
|
||||
"ui.panel.config.automation.editor.triggers.type.template.value_template"
|
||||
)}
|
||||
@ -25,7 +25,7 @@ export class HaTemplateTrigger extends LitElement {
|
||||
.value=${value_template}
|
||||
@value-changed=${this._valueChanged}
|
||||
dir="ltr"
|
||||
></ha-textarea>
|
||||
></paper-textarea>
|
||||
`;
|
||||
}
|
||||
|
||||
|
@ -33,6 +33,7 @@ import { showDomainTogglerDialog } from "../../../../dialogs/domain-toggler/show
|
||||
import "../../../../layouts/hass-loading-screen";
|
||||
import "../../../../layouts/hass-subpage";
|
||||
import type { HomeAssistant } from "../../../../types";
|
||||
import "../../../../components/ha-formfield";
|
||||
|
||||
const DEFAULT_CONFIG_EXPOSE = true;
|
||||
const IGNORE_INTERFACES = ["Alexa.EndpointHealth"];
|
||||
@ -127,14 +128,19 @@ class CloudAlexa extends LitElement {
|
||||
)
|
||||
.join(", ")}
|
||||
</state-info>
|
||||
<ha-switch
|
||||
.entityId=${entity.entity_id}
|
||||
.disabled=${!emptyFilter}
|
||||
.checked=${isExposed}
|
||||
@change=${this._exposeChanged}
|
||||
<ha-formfield
|
||||
.label=${this.hass!.localize(
|
||||
"ui.panel.config.cloud.alexa.expose"
|
||||
)}
|
||||
>
|
||||
${this.hass!.localize("ui.panel.config.cloud.alexa.expose")}
|
||||
</ha-switch>
|
||||
<ha-switch
|
||||
.entityId=${entity.entity_id}
|
||||
.disabled=${!emptyFilter}
|
||||
.checked=${isExposed}
|
||||
@change=${this._exposeChanged}
|
||||
>
|
||||
</ha-switch>
|
||||
</ha-formfield>
|
||||
</div>
|
||||
</ha-card>
|
||||
`);
|
||||
|
@ -38,6 +38,7 @@ import "../../../../layouts/hass-loading-screen";
|
||||
import "../../../../layouts/hass-subpage";
|
||||
import type { HomeAssistant } from "../../../../types";
|
||||
import { showToast } from "../../../../util/toast";
|
||||
import "../../../../components/ha-formfield";
|
||||
|
||||
const DEFAULT_CONFIG_EXPOSE = true;
|
||||
|
||||
@ -127,14 +128,19 @@ class CloudGoogleAssistant extends LitElement {
|
||||
.map((trait) => trait.substr(trait.lastIndexOf(".") + 1))
|
||||
.join(", ")}
|
||||
</state-info>
|
||||
<ha-switch
|
||||
.entityId=${entity.entity_id}
|
||||
.disabled=${!emptyFilter}
|
||||
.checked=${isExposed}
|
||||
@change=${this._exposeChanged}
|
||||
<ha-formfield
|
||||
.label=${this.hass!.localize(
|
||||
"ui.panel.config.cloud.google.expose"
|
||||
)}
|
||||
>
|
||||
${this.hass!.localize("ui.panel.config.cloud.google.expose")}
|
||||
</ha-switch>
|
||||
<ha-switch
|
||||
.entityId=${entity.entity_id}
|
||||
.disabled=${!emptyFilter}
|
||||
.checked=${isExposed}
|
||||
@change=${this._exposeChanged}
|
||||
>
|
||||
</ha-switch>
|
||||
</ha-formfield>
|
||||
${entity.might_2fa
|
||||
? html`
|
||||
<ha-switch
|
||||
|
@ -20,6 +20,7 @@ import { HomeAssistant } from "../../../types";
|
||||
import "../ha-config-section";
|
||||
import { configSections } from "../ha-panel-config";
|
||||
import "./ha-config-navigation";
|
||||
import { mdiCloudLock } from "@mdi/js";
|
||||
|
||||
@customElement("ha-config-dashboard")
|
||||
class HaConfigDashboard extends LitElement {
|
||||
@ -66,7 +67,7 @@ class HaConfigDashboard extends LitElement {
|
||||
path: "/config/cloud",
|
||||
translationKey: "ui.panel.config.cloud.caption",
|
||||
info: this.cloudStatus,
|
||||
icon: "hass:cloud-lock",
|
||||
iconPath: mdiCloudLock,
|
||||
},
|
||||
]}
|
||||
></ha-config-navigation>
|
||||
|
@ -38,7 +38,10 @@ class HaConfigNavigation extends LitElement {
|
||||
tabindex="-1"
|
||||
>
|
||||
<paper-icon-item>
|
||||
<ha-icon .icon=${page.icon} slot="item-icon"></ha-icon>
|
||||
<ha-svg-icon
|
||||
.path=${page.iconPath}
|
||||
slot="item-icon"
|
||||
></ha-svg-icon>
|
||||
<paper-item-body two-line>
|
||||
${this.hass.localize(
|
||||
page.translationKey ||
|
||||
@ -88,7 +91,7 @@ class HaConfigNavigation extends LitElement {
|
||||
display: block;
|
||||
outline: 0;
|
||||
}
|
||||
ha-icon,
|
||||
ha-svg-icon,
|
||||
ha-icon-next {
|
||||
color: var(--secondary-text-color);
|
||||
}
|
||||
|
@ -75,6 +75,7 @@ export class HaDeviceCard extends LitElement {
|
||||
: ""}
|
||||
<slot></slot>
|
||||
</div>
|
||||
<slot name="actions"></slot>
|
||||
</ha-card>
|
||||
`;
|
||||
}
|
||||
@ -100,7 +101,6 @@ export class HaDeviceCard extends LitElement {
|
||||
}
|
||||
ha-card {
|
||||
flex: 1 0 100%;
|
||||
padding-bottom: 10px;
|
||||
min-width: 0;
|
||||
}
|
||||
.device {
|
||||
|
@ -5,16 +5,17 @@ import {
|
||||
LitElement,
|
||||
property,
|
||||
TemplateResult,
|
||||
css,
|
||||
} from "lit-element";
|
||||
import { DeviceRegistryEntry } from "../../../../data/device_registry";
|
||||
import { removeMQTTDeviceEntry } from "../../../../data/mqtt";
|
||||
import { showConfirmationDialog } from "../../../../dialogs/generic/show-dialog-box";
|
||||
import { showMQTTDeviceDebugInfoDialog } from "../../../../dialogs/mqtt-device-debug-info-dialog/show-dialog-mqtt-device-debug-info";
|
||||
import { haStyle } from "../../../../resources/styles";
|
||||
import { HomeAssistant } from "../../../../types";
|
||||
import { DeviceRegistryEntry } from "../../../../../data/device_registry";
|
||||
import { removeMQTTDeviceEntry } from "../../../../../data/mqtt";
|
||||
import { showConfirmationDialog } from "../../../../../dialogs/generic/show-dialog-box";
|
||||
import { showMQTTDeviceDebugInfoDialog } from "../../../../../dialogs/mqtt-device-debug-info-dialog/show-dialog-mqtt-device-debug-info";
|
||||
import { haStyle } from "../../../../../resources/styles";
|
||||
import { HomeAssistant } from "../../../../../types";
|
||||
|
||||
@customElement("ha-device-card-mqtt")
|
||||
export class HaDeviceCardMqtt extends LitElement {
|
||||
@customElement("ha-device-actions-mqtt")
|
||||
export class HaDeviceActionsMqtt extends LitElement {
|
||||
@property() public hass!: HomeAssistant;
|
||||
|
||||
@property() public device!: DeviceRegistryEntry;
|
||||
@ -47,7 +48,15 @@ export class HaDeviceCardMqtt extends LitElement {
|
||||
await showMQTTDeviceDebugInfoDialog(this, { device });
|
||||
}
|
||||
|
||||
static get styles(): CSSResult {
|
||||
return haStyle;
|
||||
static get styles(): CSSResult[] {
|
||||
return [
|
||||
haStyle,
|
||||
css`
|
||||
:host {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
`,
|
||||
];
|
||||
}
|
||||
}
|
@ -0,0 +1,128 @@
|
||||
import {
|
||||
CSSResult,
|
||||
customElement,
|
||||
html,
|
||||
LitElement,
|
||||
property,
|
||||
TemplateResult,
|
||||
css,
|
||||
PropertyValues,
|
||||
} from "lit-element";
|
||||
import { DeviceRegistryEntry } from "../../../../../data/device_registry";
|
||||
import { haStyle } from "../../../../../resources/styles";
|
||||
import { HomeAssistant } from "../../../../../types";
|
||||
import {
|
||||
ZHADevice,
|
||||
fetchZHADevice,
|
||||
reconfigureNode,
|
||||
} from "../../../../../data/zha";
|
||||
import { navigate } from "../../../../../common/navigate";
|
||||
import { showZHADeviceZigbeeInfoDialog } from "../../../../../dialogs/zha-device-zigbee-signature-dialog/show-dialog-zha-device-zigbee-info";
|
||||
import { showConfirmationDialog } from "../../../../../dialogs/generic/show-dialog-box";
|
||||
|
||||
@customElement("ha-device-actions-zha")
|
||||
export class HaDeviceActionsZha extends LitElement {
|
||||
@property() public hass!: HomeAssistant;
|
||||
|
||||
@property() public device!: DeviceRegistryEntry;
|
||||
|
||||
@property() private _zhaDevice?: ZHADevice;
|
||||
|
||||
protected updated(changedProperties: PropertyValues) {
|
||||
if (changedProperties.has("device")) {
|
||||
const zigbeeConnection = this.device.connections.find(
|
||||
(conn) => conn[0] === "zigbee"
|
||||
);
|
||||
if (!zigbeeConnection) {
|
||||
return;
|
||||
}
|
||||
fetchZHADevice(this.hass, zigbeeConnection[1]).then((device) => {
|
||||
this._zhaDevice = device;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
protected render(): TemplateResult {
|
||||
if (!this._zhaDevice) {
|
||||
return html``;
|
||||
}
|
||||
return html`
|
||||
${this._zhaDevice.device_type !== "Coordinator"
|
||||
? html`
|
||||
<mwc-button @click=${this._onReconfigureNodeClick}>
|
||||
${this.hass!.localize(
|
||||
"ui.dialogs.zha_device_info.buttons.reconfigure"
|
||||
)}
|
||||
</mwc-button>
|
||||
`
|
||||
: ""}
|
||||
${this._zhaDevice.power_source === "Mains" &&
|
||||
(this._zhaDevice.device_type === "Router" ||
|
||||
this._zhaDevice.device_type === "Coordinator")
|
||||
? html`
|
||||
<mwc-button @click=${this._onAddDevicesClick}>
|
||||
${this.hass!.localize("ui.dialogs.zha_device_info.buttons.add")}
|
||||
</mwc-button>
|
||||
`
|
||||
: ""}
|
||||
${this._zhaDevice.device_type !== "Coordinator"
|
||||
? html`
|
||||
<mwc-button @click=${this._handleZigbeeInfoClicked}>
|
||||
${this.hass!.localize(
|
||||
"ui.dialogs.zha_device_info.buttons.zigbee_information"
|
||||
)}
|
||||
</mwc-button>
|
||||
<mwc-button class="warning" @click=${this._removeDevice}>
|
||||
${this.hass!.localize(
|
||||
"ui.dialogs.zha_device_info.buttons.remove"
|
||||
)}
|
||||
</mwc-button>
|
||||
`
|
||||
: ""}
|
||||
`;
|
||||
}
|
||||
|
||||
private async _onReconfigureNodeClick(): Promise<void> {
|
||||
if (!this.hass) {
|
||||
return;
|
||||
}
|
||||
reconfigureNode(this.hass, this._zhaDevice!.ieee);
|
||||
}
|
||||
|
||||
private _onAddDevicesClick() {
|
||||
navigate(this, "/config/zha/add/" + this._zhaDevice!.ieee);
|
||||
}
|
||||
|
||||
private async _handleZigbeeInfoClicked() {
|
||||
showZHADeviceZigbeeInfoDialog(this, { device: this._zhaDevice! });
|
||||
}
|
||||
|
||||
private async _removeDevice() {
|
||||
const confirmed = await showConfirmationDialog(this, {
|
||||
text: this.hass.localize(
|
||||
"ui.dialogs.zha_device_info.confirmations.remove"
|
||||
),
|
||||
});
|
||||
|
||||
if (!confirmed) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.hass.callService("zha", "remove", {
|
||||
ieee_address: this._zhaDevice!.ieee,
|
||||
});
|
||||
}
|
||||
|
||||
static get styles(): CSSResult[] {
|
||||
return [
|
||||
haStyle,
|
||||
css`
|
||||
:host {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
}
|
||||
`,
|
||||
];
|
||||
}
|
||||
}
|
@ -0,0 +1,93 @@
|
||||
import {
|
||||
CSSResult,
|
||||
customElement,
|
||||
html,
|
||||
LitElement,
|
||||
property,
|
||||
TemplateResult,
|
||||
css,
|
||||
PropertyValues,
|
||||
} from "lit-element";
|
||||
import { DeviceRegistryEntry } from "../../../../../data/device_registry";
|
||||
import { haStyle } from "../../../../../resources/styles";
|
||||
import { HomeAssistant } from "../../../../../types";
|
||||
import { ZHADevice, fetchZHADevice } from "../../../../../data/zha";
|
||||
import { formatAsPaddedHex } from "../../../integrations/integration-panels/zha/functions";
|
||||
|
||||
@customElement("ha-device-info-zha")
|
||||
export class HaDeviceActionsZha extends LitElement {
|
||||
@property() public hass!: HomeAssistant;
|
||||
|
||||
@property() public device!: DeviceRegistryEntry;
|
||||
|
||||
@property() private _zhaDevice?: ZHADevice;
|
||||
|
||||
protected updated(changedProperties: PropertyValues) {
|
||||
if (changedProperties.has("device")) {
|
||||
const zigbeeConnection = this.device.connections.find(
|
||||
(conn) => conn[0] === "zigbee"
|
||||
);
|
||||
if (!zigbeeConnection) {
|
||||
return;
|
||||
}
|
||||
fetchZHADevice(this.hass, zigbeeConnection[1]).then((device) => {
|
||||
this._zhaDevice = device;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
protected render(): TemplateResult {
|
||||
if (!this._zhaDevice) {
|
||||
return html``;
|
||||
}
|
||||
return html`
|
||||
<h4>Zigbee info</h4>
|
||||
<div>IEEE: ${this._zhaDevice.ieee}</div>
|
||||
<div>Nwk: ${formatAsPaddedHex(this._zhaDevice.nwk)}</div>
|
||||
<div>Device Type: ${this._zhaDevice.device_type}</div>
|
||||
<div>
|
||||
LQI:
|
||||
${this._zhaDevice.lqi ||
|
||||
this.hass!.localize("ui.dialogs.zha_device_info.unknown")}
|
||||
</div>
|
||||
<div>
|
||||
RSSI:
|
||||
${this._zhaDevice.rssi ||
|
||||
this.hass!.localize("ui.dialogs.zha_device_info.unknown")}
|
||||
</div>
|
||||
<div>
|
||||
${this.hass!.localize("ui.dialogs.zha_device_info.last_seen")}:
|
||||
${this._zhaDevice.last_seen ||
|
||||
this.hass!.localize("ui.dialogs.zha_device_info.unknown")}
|
||||
</div>
|
||||
<div>
|
||||
${this.hass!.localize("ui.dialogs.zha_device_info.power_source")}:
|
||||
${this._zhaDevice.power_source ||
|
||||
this.hass!.localize("ui.dialogs.zha_device_info.unknown")}
|
||||
</div>
|
||||
${this._zhaDevice.quirk_applied
|
||||
? html`
|
||||
<div>
|
||||
${this.hass!.localize("ui.dialogs.zha_device_info.quirk")}:
|
||||
${this._zhaDevice.quirk_class}
|
||||
</div>
|
||||
`
|
||||
: ""}
|
||||
`;
|
||||
}
|
||||
|
||||
static get styles(): CSSResult[] {
|
||||
return [
|
||||
haStyle,
|
||||
css`
|
||||
h4 {
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
div {
|
||||
word-break: break-all;
|
||||
margin-top: 2px;
|
||||
}
|
||||
`,
|
||||
];
|
||||
}
|
||||
}
|
@ -6,6 +6,7 @@ import {
|
||||
html,
|
||||
LitElement,
|
||||
property,
|
||||
TemplateResult,
|
||||
} from "lit-element";
|
||||
import { ifDefined } from "lit-html/directives/if-defined";
|
||||
import memoizeOne from "memoize-one";
|
||||
@ -38,13 +39,12 @@ import "../../../layouts/hass-tabs-subpage";
|
||||
import { HomeAssistant, Route } from "../../../types";
|
||||
import "../ha-config-section";
|
||||
import { configSections } from "../ha-panel-config";
|
||||
import "./device-detail/ha-device-card-mqtt";
|
||||
import "./device-detail/ha-device-entities-card";
|
||||
import "./device-detail/ha-device-info-card";
|
||||
import { showDeviceAutomationDialog } from "./device-detail/show-dialog-device-automation";
|
||||
|
||||
export interface EntityRegistryStateEntry extends EntityRegistryEntry {
|
||||
stateName?: string;
|
||||
stateName?: string | null;
|
||||
}
|
||||
|
||||
@customElement("ha-config-device-page")
|
||||
@ -226,16 +226,7 @@ export class HaConfigDevicePage extends LitElement {
|
||||
.devices=${this.devices}
|
||||
.device=${device}
|
||||
>
|
||||
${
|
||||
integrations.includes("mqtt")
|
||||
? html`
|
||||
<ha-device-card-mqtt
|
||||
.hass=${this.hass}
|
||||
.device=${device}
|
||||
></ha-device-card-mqtt>
|
||||
`
|
||||
: html``
|
||||
}
|
||||
${this._renderIntegrationInfo(device, integrations)}
|
||||
</ha-device-info-card>
|
||||
|
||||
${
|
||||
@ -439,7 +430,7 @@ export class HaConfigDevicePage extends LitElement {
|
||||
</hass-tabs-subpage> `;
|
||||
}
|
||||
|
||||
private _computeEntityName(entity) {
|
||||
private _computeEntityName(entity: EntityRegistryEntry) {
|
||||
if (entity.name) {
|
||||
return entity.name;
|
||||
}
|
||||
@ -480,6 +471,41 @@ export class HaConfigDevicePage extends LitElement {
|
||||
});
|
||||
}
|
||||
|
||||
private _renderIntegrationInfo(
|
||||
device,
|
||||
integrations: string[]
|
||||
): TemplateResult[] {
|
||||
const templates: TemplateResult[] = [];
|
||||
if (integrations.includes("mqtt")) {
|
||||
import("./device-detail/integration-elements/ha-device-actions-mqtt");
|
||||
templates.push(html`
|
||||
<div class="card-actions" slot="actions">
|
||||
<ha-device-actions-mqtt
|
||||
.hass=${this.hass}
|
||||
.device=${device}
|
||||
></ha-device-actions-mqtt>
|
||||
</div>
|
||||
`);
|
||||
}
|
||||
if (integrations.includes("zha")) {
|
||||
import("./device-detail/integration-elements/ha-device-actions-zha");
|
||||
import("./device-detail/integration-elements/ha-device-info-zha");
|
||||
templates.push(html`
|
||||
<ha-device-info-zha
|
||||
.hass=${this.hass}
|
||||
.device=${device}
|
||||
></ha-device-info-zha>
|
||||
<div class="card-actions" slot="actions">
|
||||
<ha-device-actions-zha
|
||||
.hass=${this.hass}
|
||||
.device=${device}
|
||||
></ha-device-actions-zha>
|
||||
</div>
|
||||
`);
|
||||
}
|
||||
return templates;
|
||||
}
|
||||
|
||||
private async _showSettings() {
|
||||
const device = this._device(this.deviceId, this.devices)!;
|
||||
showDeviceRegistryDetailDialog(this, {
|
||||
|
@ -88,31 +88,31 @@ export class HaEntityRegistryBasicEditor extends LitElement {
|
||||
.checked=${!this._disabledBy}
|
||||
@change=${this._disabledByChanged}
|
||||
>
|
||||
<div>
|
||||
<div>
|
||||
${this.hass.localize(
|
||||
"ui.dialogs.entity_registry.editor.enabled_label"
|
||||
)}
|
||||
</div>
|
||||
<div class="secondary">
|
||||
${this._disabledBy && this._disabledBy !== "user"
|
||||
? this.hass.localize(
|
||||
"ui.dialogs.entity_registry.editor.enabled_cause",
|
||||
"cause",
|
||||
this.hass.localize(
|
||||
`config_entry.disabled_by.${this._disabledBy}`
|
||||
)
|
||||
)
|
||||
: ""}
|
||||
${this.hass.localize(
|
||||
"ui.dialogs.entity_registry.editor.enabled_description"
|
||||
)}
|
||||
<br />${this.hass.localize(
|
||||
"ui.dialogs.entity_registry.editor.note"
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</ha-switch>
|
||||
<div>
|
||||
<div>
|
||||
${this.hass.localize(
|
||||
"ui.dialogs.entity_registry.editor.enabled_label"
|
||||
)}
|
||||
</div>
|
||||
<div class="secondary">
|
||||
${this._disabledBy && this._disabledBy !== "user"
|
||||
? this.hass.localize(
|
||||
"ui.dialogs.entity_registry.editor.enabled_cause",
|
||||
"cause",
|
||||
this.hass.localize(
|
||||
`config_entry.disabled_by.${this._disabledBy}`
|
||||
)
|
||||
)
|
||||
: ""}
|
||||
${this.hass.localize(
|
||||
"ui.dialogs.entity_registry.editor.enabled_description"
|
||||
)}
|
||||
<br />${this.hass.localize(
|
||||
"ui.dialogs.entity_registry.editor.note"
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
@ -127,9 +127,14 @@ export class HaEntityRegistryBasicEditor extends LitElement {
|
||||
|
||||
static get styles() {
|
||||
return css`
|
||||
ha-switch {
|
||||
margin-right: 16px;
|
||||
}
|
||||
.row {
|
||||
margin-top: 8px;
|
||||
color: var(--primary-text-color);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
.secondary {
|
||||
color: var(--secondary-text-color);
|
||||
|
@ -120,31 +120,31 @@ export class EntityRegistrySettings extends LitElement {
|
||||
.checked=${!this._disabledBy}
|
||||
@change=${this._disabledByChanged}
|
||||
>
|
||||
<div>
|
||||
<div>
|
||||
${this.hass.localize(
|
||||
"ui.dialogs.entity_registry.editor.enabled_label"
|
||||
)}
|
||||
</div>
|
||||
<div class="secondary">
|
||||
${this._disabledBy && this._disabledBy !== "user"
|
||||
? this.hass.localize(
|
||||
"ui.dialogs.entity_registry.editor.enabled_cause",
|
||||
"cause",
|
||||
this.hass.localize(
|
||||
`config_entry.disabled_by.${this._disabledBy}`
|
||||
)
|
||||
)
|
||||
: ""}
|
||||
${this.hass.localize(
|
||||
"ui.dialogs.entity_registry.editor.enabled_description"
|
||||
)}
|
||||
<br />${this.hass.localize(
|
||||
"ui.dialogs.entity_registry.editor.note"
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</ha-switch>
|
||||
<div>
|
||||
<div>
|
||||
${this.hass.localize(
|
||||
"ui.dialogs.entity_registry.editor.enabled_label"
|
||||
)}
|
||||
</div>
|
||||
<div class="secondary">
|
||||
${this._disabledBy && this._disabledBy !== "user"
|
||||
? this.hass.localize(
|
||||
"ui.dialogs.entity_registry.editor.enabled_cause",
|
||||
"cause",
|
||||
this.hass.localize(
|
||||
`config_entry.disabled_by.${this._disabledBy}`
|
||||
)
|
||||
)
|
||||
: ""}
|
||||
${this.hass.localize(
|
||||
"ui.dialogs.entity_registry.editor.enabled_description"
|
||||
)}
|
||||
<br />${this.hass.localize(
|
||||
"ui.dialogs.entity_registry.editor.note"
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</paper-dialog-scrollable>
|
||||
@ -247,9 +247,14 @@ export class EntityRegistrySettings extends LitElement {
|
||||
mwc-button.warning {
|
||||
margin-right: auto;
|
||||
}
|
||||
ha-switch {
|
||||
margin-right: 16px;
|
||||
}
|
||||
.row {
|
||||
margin-top: 8px;
|
||||
color: var(--primary-text-color);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
`,
|
||||
];
|
||||
|
@ -1,3 +1,4 @@
|
||||
import "@material/mwc-list/mwc-list-item";
|
||||
import "@polymer/paper-checkbox/paper-checkbox";
|
||||
import "@polymer/paper-dropdown-menu/paper-dropdown-menu";
|
||||
import "@polymer/paper-item/paper-icon-item";
|
||||
@ -31,6 +32,7 @@ import type {
|
||||
RowClickedEvent,
|
||||
SelectionChangedEvent,
|
||||
} from "../../../components/data-table/ha-data-table";
|
||||
import "../../../components/ha-button-menu";
|
||||
import "../../../components/ha-icon";
|
||||
import { ConfigEntry, getConfigEntries } from "../../../data/config_entries";
|
||||
import {
|
||||
@ -53,6 +55,7 @@ import {
|
||||
loadEntityEditorDialog,
|
||||
showEntityEditorDialog,
|
||||
} from "./show-dialog-entity-editor";
|
||||
import { mdiFilterVariant } from "@mdi/js";
|
||||
|
||||
export interface StateEntity extends EntityRegistryEntry {
|
||||
readonly?: boolean;
|
||||
@ -442,47 +445,55 @@ export class HaConfigEntities extends SubscribeMixin(LitElement) {
|
||||
>
|
||||
</div>`
|
||||
: ""}
|
||||
<paper-menu-button no-animations horizontal-align="right">
|
||||
<ha-icon-button
|
||||
aria-label=${this.hass!.localize(
|
||||
<ha-button-menu corner="BOTTOM_START">
|
||||
<mwc-icon-button
|
||||
slot="trigger"
|
||||
.label=${this.hass!.localize(
|
||||
"ui.panel.config.entities.picker.filter.filter"
|
||||
)}
|
||||
title="${this.hass!.localize(
|
||||
.title=${this.hass!.localize(
|
||||
"ui.panel.config.entities.picker.filter.filter"
|
||||
)}"
|
||||
icon="hass:filter-variant"
|
||||
slot="dropdown-trigger"
|
||||
></ha-icon-button>
|
||||
<paper-listbox slot="dropdown-content">
|
||||
<paper-icon-item @tap="${this._showDisabledChanged}">
|
||||
<paper-checkbox
|
||||
.checked=${this._showDisabled}
|
||||
slot="item-icon"
|
||||
></paper-checkbox>
|
||||
${this.hass!.localize(
|
||||
"ui.panel.config.entities.picker.filter.show_disabled"
|
||||
)}
|
||||
</paper-icon-item>
|
||||
<paper-icon-item @tap="${this._showRestoredChanged}">
|
||||
<paper-checkbox
|
||||
.checked=${this._showUnavailable}
|
||||
slot="item-icon"
|
||||
></paper-checkbox>
|
||||
${this.hass!.localize(
|
||||
"ui.panel.config.entities.picker.filter.show_unavailable"
|
||||
)}
|
||||
</paper-icon-item>
|
||||
<paper-icon-item @tap="${this._showReadOnlyChanged}">
|
||||
<paper-checkbox
|
||||
.checked=${this._showReadOnly}
|
||||
slot="item-icon"
|
||||
></paper-checkbox>
|
||||
${this.hass!.localize(
|
||||
"ui.panel.config.entities.picker.filter.show_readonly"
|
||||
)}
|
||||
</paper-icon-item>
|
||||
</paper-listbox>
|
||||
</paper-menu-button>
|
||||
)}
|
||||
>
|
||||
<ha-svg-icon path=${mdiFilterVariant}></ha-svg-icon>
|
||||
</mwc-icon-button>
|
||||
<mwc-list-item
|
||||
@click="${this._showDisabledChanged}"
|
||||
graphic="control"
|
||||
>
|
||||
<ha-checkbox
|
||||
slot="graphic"
|
||||
.checked=${this._showDisabled}
|
||||
></ha-checkbox>
|
||||
${this.hass!.localize(
|
||||
"ui.panel.config.entities.picker.filter.show_disabled"
|
||||
)}
|
||||
</mwc-list-item>
|
||||
<mwc-list-item
|
||||
@click="${this._showRestoredChanged}"
|
||||
graphic="control"
|
||||
>
|
||||
<ha-checkbox
|
||||
slot="graphic"
|
||||
.checked=${this._showUnavailable}
|
||||
></ha-checkbox>
|
||||
${this.hass!.localize(
|
||||
"ui.panel.config.entities.picker.filter.show_unavailable"
|
||||
)}
|
||||
</mwc-list-item>
|
||||
<mwc-list-item
|
||||
@click="${this._showReadOnlyChanged}"
|
||||
graphic="control"
|
||||
>
|
||||
<ha-checkbox
|
||||
slot="graphic"
|
||||
.checked=${this._showReadOnly}
|
||||
></ha-checkbox>
|
||||
${this.hass!.localize(
|
||||
"ui.panel.config.entities.picker.filter.show_readonly"
|
||||
)}
|
||||
</mwc-list-item>
|
||||
</ha-button-menu>
|
||||
`;
|
||||
|
||||
return html`
|
||||
@ -730,8 +741,8 @@ export class HaConfigEntities extends SubscribeMixin(LitElement) {
|
||||
height: calc(100vh - 65px);
|
||||
display: block;
|
||||
}
|
||||
ha-switch {
|
||||
margin-top: 16px;
|
||||
ha-button-menu {
|
||||
margin-right: 8px;
|
||||
}
|
||||
.table-header {
|
||||
display: flex;
|
||||
|
@ -9,6 +9,25 @@ import "../../layouts/hass-loading-screen";
|
||||
import { HassRouterPage, RouterOptions } from "../../layouts/hass-router-page";
|
||||
import { PageNavigation } from "../../layouts/hass-tabs-subpage";
|
||||
import { HomeAssistant, Route } from "../../types";
|
||||
import {
|
||||
mdiPuzzle,
|
||||
mdiDevices,
|
||||
mdiShape,
|
||||
mdiSofa,
|
||||
mdiRobot,
|
||||
mdiPalette,
|
||||
mdiScriptText,
|
||||
mdiTools,
|
||||
mdiViewDashboard,
|
||||
mdiAccount,
|
||||
mdiMapMarkerRadius,
|
||||
mdiAccountBadgeHorizontal,
|
||||
mdiHomeAssistant,
|
||||
mdiServer,
|
||||
mdiInformation,
|
||||
mdiMathLog,
|
||||
mdiPencil,
|
||||
} from "@mdi/js";
|
||||
|
||||
declare global {
|
||||
// for fire event
|
||||
@ -23,28 +42,28 @@ export const configSections: { [name: string]: PageNavigation[] } = {
|
||||
component: "integrations",
|
||||
path: "/config/integrations",
|
||||
translationKey: "ui.panel.config.integrations.caption",
|
||||
icon: "hass:puzzle",
|
||||
iconPath: mdiPuzzle,
|
||||
core: true,
|
||||
},
|
||||
{
|
||||
component: "devices",
|
||||
path: "/config/devices",
|
||||
translationKey: "ui.panel.config.devices.caption",
|
||||
icon: "hass:devices",
|
||||
iconPath: mdiDevices,
|
||||
core: true,
|
||||
},
|
||||
{
|
||||
component: "entities",
|
||||
path: "/config/entities",
|
||||
translationKey: "ui.panel.config.entities.caption",
|
||||
icon: "hass:shape",
|
||||
iconPath: mdiShape,
|
||||
core: true,
|
||||
},
|
||||
{
|
||||
component: "areas",
|
||||
path: "/config/areas",
|
||||
translationKey: "ui.panel.config.areas.caption",
|
||||
icon: "hass:sofa",
|
||||
iconPath: mdiSofa,
|
||||
core: true,
|
||||
},
|
||||
],
|
||||
@ -53,25 +72,25 @@ export const configSections: { [name: string]: PageNavigation[] } = {
|
||||
component: "automation",
|
||||
path: "/config/automation",
|
||||
translationKey: "ui.panel.config.automation.caption",
|
||||
icon: "hass:robot",
|
||||
iconPath: mdiRobot,
|
||||
},
|
||||
{
|
||||
component: "scene",
|
||||
path: "/config/scene",
|
||||
translationKey: "ui.panel.config.scene.caption",
|
||||
icon: "hass:palette",
|
||||
iconPath: mdiPalette,
|
||||
},
|
||||
{
|
||||
component: "script",
|
||||
path: "/config/script",
|
||||
translationKey: "ui.panel.config.script.caption",
|
||||
icon: "hass:script-text",
|
||||
iconPath: mdiScriptText,
|
||||
},
|
||||
{
|
||||
component: "helpers",
|
||||
path: "/config/helpers",
|
||||
translationKey: "ui.panel.config.helpers.caption",
|
||||
icon: "hass:tools",
|
||||
iconPath: mdiTools,
|
||||
core: true,
|
||||
},
|
||||
],
|
||||
@ -80,7 +99,7 @@ export const configSections: { [name: string]: PageNavigation[] } = {
|
||||
component: "lovelace",
|
||||
path: "/config/lovelace/dashboards",
|
||||
translationKey: "ui.panel.config.lovelace.caption",
|
||||
icon: "hass:view-dashboard",
|
||||
iconPath: mdiViewDashboard,
|
||||
},
|
||||
],
|
||||
persons: [
|
||||
@ -88,19 +107,19 @@ export const configSections: { [name: string]: PageNavigation[] } = {
|
||||
component: "person",
|
||||
path: "/config/person",
|
||||
translationKey: "ui.panel.config.person.caption",
|
||||
icon: "hass:account",
|
||||
iconPath: mdiAccount,
|
||||
},
|
||||
{
|
||||
component: "zone",
|
||||
path: "/config/zone",
|
||||
translationKey: "ui.panel.config.zone.caption",
|
||||
icon: "hass:map-marker-radius",
|
||||
iconPath: mdiMapMarkerRadius,
|
||||
},
|
||||
{
|
||||
component: "users",
|
||||
path: "/config/users",
|
||||
translationKey: "ui.panel.config.users.caption",
|
||||
icon: "hass:account-badge-horizontal",
|
||||
iconPath: mdiAccountBadgeHorizontal,
|
||||
core: true,
|
||||
},
|
||||
],
|
||||
@ -109,39 +128,41 @@ export const configSections: { [name: string]: PageNavigation[] } = {
|
||||
component: "core",
|
||||
path: "/config/core",
|
||||
translationKey: "ui.panel.config.core.caption",
|
||||
icon: "hass:home-assistant",
|
||||
iconPath: mdiHomeAssistant,
|
||||
core: true,
|
||||
},
|
||||
{
|
||||
component: "server_control",
|
||||
path: "/config/server_control",
|
||||
translationKey: "ui.panel.config.server_control.caption",
|
||||
icon: "hass:server",
|
||||
iconPath: mdiServer,
|
||||
core: true,
|
||||
},
|
||||
{
|
||||
component: "logs",
|
||||
path: "/config/logs",
|
||||
translationKey: "ui.panel.config.logs.caption",
|
||||
iconPath: mdiMathLog,
|
||||
core: true,
|
||||
},
|
||||
{
|
||||
component: "info",
|
||||
path: "/config/info",
|
||||
translationKey: "ui.panel.config.info.caption",
|
||||
iconPath: mdiInformation,
|
||||
core: true,
|
||||
},
|
||||
],
|
||||
advanced: [
|
||||
{
|
||||
component: "customize",
|
||||
path: "/config/customize",
|
||||
translationKey: "ui.panel.config.customize.caption",
|
||||
icon: "hass:pencil",
|
||||
iconPath: mdiPencil,
|
||||
core: true,
|
||||
advancedOnly: true,
|
||||
},
|
||||
],
|
||||
other: [
|
||||
{
|
||||
component: "zha",
|
||||
path: "/config/zha",
|
||||
translationKey: "component.zha.title",
|
||||
icon: "hass:zigbee",
|
||||
},
|
||||
{
|
||||
component: "zwave",
|
||||
path: "/config/zwave",
|
||||
translationKey: "component.zwave.title",
|
||||
icon: "hass:z-wave",
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
@customElement("ha-panel-config")
|
||||
@ -197,6 +218,20 @@ class HaPanelConfig extends HassRouterPage {
|
||||
/* webpackChunkName: "panel-config-server-control" */ "./server_control/ha-config-server-control"
|
||||
),
|
||||
},
|
||||
logs: {
|
||||
tag: "ha-config-logs",
|
||||
load: () =>
|
||||
import(
|
||||
/* webpackChunkName: "panel-config-logs" */ "./logs/ha-config-logs"
|
||||
),
|
||||
},
|
||||
info: {
|
||||
tag: "ha-config-info",
|
||||
load: () =>
|
||||
import(
|
||||
/* webpackChunkName: "panel-config-info" */ "./info/ha-config-info"
|
||||
),
|
||||
},
|
||||
customize: {
|
||||
tag: "ha-config-customize",
|
||||
load: () =>
|
||||
@ -278,14 +313,14 @@ class HaPanelConfig extends HassRouterPage {
|
||||
tag: "zha-config-dashboard-router",
|
||||
load: () =>
|
||||
import(
|
||||
/* webpackChunkName: "panel-config-zha" */ "./zha/zha-config-dashboard-router"
|
||||
/* webpackChunkName: "panel-config-zha" */ "./integrations/integration-panels/zha/zha-config-dashboard-router"
|
||||
),
|
||||
},
|
||||
zwave: {
|
||||
tag: "ha-config-zwave",
|
||||
load: () =>
|
||||
import(
|
||||
/* webpackChunkName: "panel-config-zwave" */ "./zwave/ha-config-zwave"
|
||||
/* webpackChunkName: "panel-config-zwave" */ "./integrations/integration-panels/zwave/ha-config-zwave"
|
||||
),
|
||||
},
|
||||
},
|
||||
|
@ -10,7 +10,6 @@ import {
|
||||
} from "lit-element";
|
||||
import { fireEvent } from "../../../../common/dom/fire_event";
|
||||
import "../../../../components/ha-icon-input";
|
||||
import "../../../../components/ha-switch";
|
||||
import { InputBoolean } from "../../../../data/input_boolean";
|
||||
import { haStyle } from "../../../../resources/styles";
|
||||
import { HomeAssistant } from "../../../../types";
|
||||
|
@ -12,7 +12,6 @@ import {
|
||||
} from "lit-element";
|
||||
import { fireEvent } from "../../../../common/dom/fire_event";
|
||||
import "../../../../components/ha-icon-input";
|
||||
import "../../../../components/ha-switch";
|
||||
import { InputDateTime } from "../../../../data/input_datetime";
|
||||
import { haStyle } from "../../../../resources/styles";
|
||||
import { HomeAssistant } from "../../../../types";
|
||||
|
@ -12,7 +12,6 @@ import {
|
||||
} from "lit-element";
|
||||
import { fireEvent } from "../../../../common/dom/fire_event";
|
||||
import "../../../../components/ha-icon-input";
|
||||
import "../../../../components/ha-switch";
|
||||
import { InputNumber } from "../../../../data/input_number";
|
||||
import { haStyle } from "../../../../resources/styles";
|
||||
import { HomeAssistant } from "../../../../types";
|
||||
|
@ -16,7 +16,6 @@ import {
|
||||
} from "lit-element";
|
||||
import { fireEvent } from "../../../../common/dom/fire_event";
|
||||
import "../../../../components/ha-icon-input";
|
||||
import "../../../../components/ha-switch";
|
||||
import type { InputSelect } from "../../../../data/input_select";
|
||||
import { showConfirmationDialog } from "../../../../dialogs/generic/show-dialog-box";
|
||||
import { haStyle } from "../../../../resources/styles";
|
||||
|
@ -12,7 +12,6 @@ import {
|
||||
} from "lit-element";
|
||||
import { fireEvent } from "../../../../common/dom/fire_event";
|
||||
import "../../../../components/ha-icon-input";
|
||||
import "../../../../components/ha-switch";
|
||||
import { InputText } from "../../../../data/input_text";
|
||||
import { haStyle } from "../../../../resources/styles";
|
||||
import { HomeAssistant } from "../../../../types";
|
||||
|
209
src/panels/config/info/ha-config-info.ts
Normal file
209
src/panels/config/info/ha-config-info.ts
Normal file
@ -0,0 +1,209 @@
|
||||
import {
|
||||
css,
|
||||
CSSResult,
|
||||
html,
|
||||
LitElement,
|
||||
property,
|
||||
TemplateResult,
|
||||
} from "lit-element";
|
||||
import { haStyle } from "../../../resources/styles";
|
||||
import { HomeAssistant, Route } from "../../../types";
|
||||
import "./integrations-card";
|
||||
import "./system-health-card";
|
||||
import { configSections } from "../ha-panel-config";
|
||||
import "../../../layouts/hass-tabs-subpage";
|
||||
|
||||
const JS_TYPE = __BUILD__;
|
||||
const JS_VERSION = __VERSION__;
|
||||
|
||||
class HaConfigInfo extends LitElement {
|
||||
@property() public hass!: HomeAssistant;
|
||||
|
||||
@property() public narrow!: boolean;
|
||||
|
||||
@property() public isWide!: boolean;
|
||||
|
||||
@property() public showAdvanced!: boolean;
|
||||
|
||||
@property() public route!: Route;
|
||||
|
||||
protected render(): TemplateResult {
|
||||
const hass = this.hass;
|
||||
const customUiList: Array<{ name: string; url: string; version: string }> =
|
||||
(window as any).CUSTOM_UI_LIST || [];
|
||||
|
||||
return html`
|
||||
<hass-tabs-subpage
|
||||
.hass=${this.hass}
|
||||
.narrow=${this.narrow}
|
||||
back-path="/config"
|
||||
.route=${this.route}
|
||||
.tabs=${configSections.general}
|
||||
>
|
||||
<div class="about">
|
||||
<a
|
||||
href="https://www.home-assistant.io"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
><img
|
||||
src="/static/icons/favicon-192x192.png"
|
||||
height="192"
|
||||
alt="${this.hass.localize(
|
||||
"ui.panel.config.info.home_assistant_logo"
|
||||
)}"
|
||||
/></a>
|
||||
<br />
|
||||
<h2>Home Assistant ${hass.connection.haVersion}</h2>
|
||||
<p>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.info.path_configuration",
|
||||
"path",
|
||||
hass.config.config_dir
|
||||
)}
|
||||
</p>
|
||||
<p class="develop">
|
||||
<a
|
||||
href="https://www.home-assistant.io/developers/credits/"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
${this.hass.localize("ui.panel.config.info.developed_by")}
|
||||
</a>
|
||||
</p>
|
||||
<p>
|
||||
${this.hass.localize("ui.panel.config.info.license")}<br />
|
||||
${this.hass.localize("ui.panel.config.info.source")}
|
||||
<a
|
||||
href="https://github.com/home-assistant/core"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>${this.hass.localize("ui.panel.config.info.server")}</a
|
||||
>
|
||||
—
|
||||
<a
|
||||
href="https://github.com/home-assistant/frontend"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>${this.hass.localize("ui.panel.config.info.frontend")}</a
|
||||
>
|
||||
</p>
|
||||
<p>
|
||||
${this.hass.localize("ui.panel.config.info.built_using")}
|
||||
<a href="https://www.python.org" target="_blank" rel="noreferrer"
|
||||
>Python 3</a
|
||||
>,
|
||||
<a
|
||||
href="https://www.polymer-project.org"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>Polymer</a
|
||||
>, ${this.hass.localize("ui.panel.config.info.icons_by")}
|
||||
<a
|
||||
href="https://www.google.com/design/icons/"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>Google</a
|
||||
>
|
||||
${this.hass.localize("ui.common.and")}
|
||||
<a
|
||||
href="https://MaterialDesignIcons.com"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>MaterialDesignIcons.com</a
|
||||
>.
|
||||
</p>
|
||||
<p>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.info.frontend_version",
|
||||
"version",
|
||||
JS_VERSION,
|
||||
"type",
|
||||
JS_TYPE
|
||||
)}
|
||||
${customUiList.length > 0
|
||||
? html`
|
||||
<div>
|
||||
${this.hass.localize("ui.panel.config.info.custom_uis")}
|
||||
${customUiList.map(
|
||||
(item) => html`
|
||||
<div>
|
||||
<a href="${item.url}" target="_blank"> ${item.name}</a
|
||||
>: ${item.version}
|
||||
</div>
|
||||
`
|
||||
)}
|
||||
</div>
|
||||
`
|
||||
: ""}
|
||||
</p>
|
||||
</div>
|
||||
<div class="content">
|
||||
<system-health-card .hass=${this.hass}></system-health-card>
|
||||
<integrations-card .hass=${this.hass}></integrations-card>
|
||||
</div>
|
||||
</hass-tabs-subpage>
|
||||
`;
|
||||
}
|
||||
|
||||
protected firstUpdated(changedProps): void {
|
||||
super.firstUpdated(changedProps);
|
||||
|
||||
// Legacy custom UI can be slow to register, give them time.
|
||||
const customUI = ((window as any).CUSTOM_UI_LIST || []).length;
|
||||
setTimeout(() => {
|
||||
if (((window as any).CUSTOM_UI_LIST || []).length !== customUI.length) {
|
||||
this.requestUpdate();
|
||||
}
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
static get styles(): CSSResult[] {
|
||||
return [
|
||||
haStyle,
|
||||
css`
|
||||
:host {
|
||||
-ms-user-select: initial;
|
||||
-webkit-user-select: initial;
|
||||
-moz-user-select: initial;
|
||||
}
|
||||
|
||||
.content {
|
||||
direction: ltr;
|
||||
}
|
||||
|
||||
.about {
|
||||
text-align: center;
|
||||
line-height: 2em;
|
||||
}
|
||||
|
||||
.version {
|
||||
@apply --paper-font-headline;
|
||||
}
|
||||
|
||||
.develop {
|
||||
@apply --paper-font-subhead;
|
||||
}
|
||||
|
||||
.about a {
|
||||
color: var(--primary-color);
|
||||
}
|
||||
|
||||
system-health-card,
|
||||
integrations-card {
|
||||
display: block;
|
||||
max-width: 600px;
|
||||
margin: 0 auto;
|
||||
padding-bottom: 16px;
|
||||
}
|
||||
`,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"ha-config-info": HaConfigInfo;
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define("ha-config-info", HaConfigInfo);
|
@ -35,9 +35,7 @@ class IntegrationsCard extends LitElement {
|
||||
protected render(): TemplateResult {
|
||||
return html`
|
||||
<ha-card
|
||||
.header=${this.hass.localize(
|
||||
"ui.panel.developer-tools.tabs.info.integrations"
|
||||
)}
|
||||
.header=${this.hass.localize("ui.panel.config.info.integrations")}
|
||||
>
|
||||
<table class="card-content">
|
||||
<tbody>
|
||||
@ -67,7 +65,7 @@ class IntegrationsCard extends LitElement {
|
||||
rel="noreferrer"
|
||||
>
|
||||
${this.hass.localize(
|
||||
"ui.panel.developer-tools.tabs.info.documentation"
|
||||
"ui.panel.config.info.documentation"
|
||||
)}
|
||||
</a>
|
||||
</td>
|
||||
@ -83,7 +81,7 @@ class IntegrationsCard extends LitElement {
|
||||
rel="noreferrer"
|
||||
>
|
||||
${this.hass.localize(
|
||||
"ui.panel.developer-tools.tabs.info.issues"
|
||||
"ui.panel.config.info.issues"
|
||||
)}
|
||||
</a>
|
||||
</td>
|
@ -97,9 +97,7 @@ class SystemHealthCard extends LitElement {
|
||||
} catch (err) {
|
||||
this._info = {
|
||||
system_health: {
|
||||
error: this.hass.localize(
|
||||
"ui.panel.developer-tools.tabs.info.system_health_error"
|
||||
),
|
||||
error: this.hass.localize("ui.panel.config.info.system_health_error"),
|
||||
},
|
||||
};
|
||||
}
|
@ -1,4 +1,9 @@
|
||||
import "@material/mwc-fab";
|
||||
import "@material/mwc-icon-button";
|
||||
import "@material/mwc-list/mwc-list-item";
|
||||
import { mdiDotsVertical, mdiPlus } from "@mdi/js";
|
||||
import "@polymer/app-route/app-route";
|
||||
import Fuse from "fuse.js";
|
||||
import { UnsubscribeFunc } from "home-assistant-js-websocket";
|
||||
import {
|
||||
css,
|
||||
@ -11,13 +16,16 @@ import {
|
||||
TemplateResult,
|
||||
} from "lit-element";
|
||||
import memoizeOne from "memoize-one";
|
||||
import Fuse from "fuse.js";
|
||||
import { HASSDomEvent } from "../../../common/dom/fire_event";
|
||||
import "../../../common/search/search-input";
|
||||
import { caseInsensitiveCompare } from "../../../common/string/compare";
|
||||
import { LocalizeFunc } from "../../../common/translations/localize";
|
||||
import { computeRTL } from "../../../common/util/compute_rtl";
|
||||
import { nextRender } from "../../../common/util/render-status";
|
||||
import "../../../components/entity/ha-state-icon";
|
||||
import "../../../components/ha-button-menu";
|
||||
import "../../../components/ha-card";
|
||||
import "@material/mwc-fab";
|
||||
import "../../../components/ha-svg-icon";
|
||||
import {
|
||||
ConfigEntry,
|
||||
deleteConfigEntry,
|
||||
@ -42,23 +50,18 @@ import {
|
||||
import { domainToName } from "../../../data/integration";
|
||||
import { showConfigFlowDialog } from "../../../dialogs/config-flow/show-dialog-config-flow";
|
||||
import { showConfirmationDialog } from "../../../dialogs/generic/show-dialog-box";
|
||||
import "../../../layouts/hass-tabs-subpage";
|
||||
import "../../../layouts/hass-loading-screen";
|
||||
import "../../../layouts/hass-tabs-subpage";
|
||||
import { SubscribeMixin } from "../../../mixins/subscribe-mixin";
|
||||
import { haStyle } from "../../../resources/styles";
|
||||
import { HomeAssistant, Route } from "../../../types";
|
||||
import { configSections } from "../ha-panel-config";
|
||||
import "../../../common/search/search-input";
|
||||
import "./ha-integration-card";
|
||||
import type {
|
||||
ConfigEntryRemovedEvent,
|
||||
ConfigEntryUpdatedEvent,
|
||||
HaIntegrationCard,
|
||||
} from "./ha-integration-card";
|
||||
import { HASSDomEvent } from "../../../common/dom/fire_event";
|
||||
import "../../../components/ha-svg-icon";
|
||||
import { mdiPlus } from "@mdi/js";
|
||||
import { LocalizeFunc } from "../../../common/translations/localize";
|
||||
|
||||
interface DataEntryFlowProgressExtended extends DataEntryFlowProgress {
|
||||
localized_title?: string;
|
||||
@ -258,28 +261,22 @@ class HaConfigIntegrations extends SubscribeMixin(LitElement) {
|
||||
</div>
|
||||
`
|
||||
: ""}
|
||||
<paper-menu-button
|
||||
close-on-activate
|
||||
no-animations
|
||||
horizontal-align="right"
|
||||
horizontal-offset="-5"
|
||||
slot="toolbar-icon"
|
||||
>
|
||||
<ha-icon-button
|
||||
icon="hass:dots-vertical"
|
||||
slot="dropdown-trigger"
|
||||
alt="menu"
|
||||
></ha-icon-button>
|
||||
<paper-listbox slot="dropdown-content" role="listbox">
|
||||
<paper-item @tap=${this._toggleShowIgnored}>
|
||||
${this.hass.localize(
|
||||
this._showIgnored
|
||||
? "ui.panel.config.integrations.ignore.hide_ignored"
|
||||
: "ui.panel.config.integrations.ignore.show_ignored"
|
||||
)}
|
||||
</paper-item>
|
||||
</paper-listbox>
|
||||
</paper-menu-button>
|
||||
<ha-button-menu corner="BOTTOM_START" slot="toolbar-icon">
|
||||
<mwc-icon-button
|
||||
.title=${this.hass.localize("ui.common.menu")}
|
||||
.label=${this.hass.localize("ui.common.overflow_menu")}
|
||||
slot="trigger"
|
||||
>
|
||||
<ha-svg-icon path=${mdiDotsVertical}></ha-svg-icon>
|
||||
</mwc-icon-button>
|
||||
<mwc-list-item @click=${this._toggleShowIgnored}>
|
||||
${this.hass.localize(
|
||||
this._showIgnored
|
||||
? "ui.panel.config.integrations.ignore.hide_ignored"
|
||||
: "ui.panel.config.integrations.ignore.show_ignored"
|
||||
)}
|
||||
</mwc-list-item>
|
||||
</ha-button-menu>
|
||||
|
||||
${!this.narrow
|
||||
? html`
|
||||
@ -302,7 +299,7 @@ class HaConfigIntegrations extends SubscribeMixin(LitElement) {
|
||||
${this._showIgnored
|
||||
? ignoredConfigEntries.map(
|
||||
(item: ConfigEntryExtended) => html`
|
||||
<ha-card class="ignored">
|
||||
<ha-card outlined class="ignored">
|
||||
<div class="header">
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.integrations.ignore.ignored"
|
||||
@ -338,7 +335,7 @@ class HaConfigIntegrations extends SubscribeMixin(LitElement) {
|
||||
${configEntriesInProgress.length
|
||||
? configEntriesInProgress.map(
|
||||
(flow: DataEntryFlowProgressExtended) => html`
|
||||
<ha-card class="discovered">
|
||||
<ha-card outlined class="discovered">
|
||||
<div class="header">
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.integrations.discovered"
|
||||
@ -399,7 +396,7 @@ class HaConfigIntegrations extends SubscribeMixin(LitElement) {
|
||||
)
|
||||
: !this._configEntries.length
|
||||
? html`
|
||||
<ha-card>
|
||||
<ha-card outlined>
|
||||
<div class="card-content">
|
||||
<h1>
|
||||
${this.hass.localize("ui.panel.config.integrations.none")}
|
||||
@ -616,7 +613,7 @@ class HaConfigIntegrations extends SubscribeMixin(LitElement) {
|
||||
justify-content: space-between;
|
||||
}
|
||||
.discovered {
|
||||
border: 1px solid var(--primary-color);
|
||||
--ha-card-border-color: var(--primary-color);
|
||||
}
|
||||
.discovered .header {
|
||||
background: var(--primary-color);
|
||||
@ -625,7 +622,7 @@ class HaConfigIntegrations extends SubscribeMixin(LitElement) {
|
||||
text-align: center;
|
||||
}
|
||||
.ignored {
|
||||
border: 1px solid var(--light-theme-disabled-color);
|
||||
--ha-card-border-color: var(--light-theme-disabled-color);
|
||||
}
|
||||
.ignored img {
|
||||
filter: grayscale(1);
|
||||
|
@ -27,6 +27,7 @@ import {
|
||||
import { haStyle } from "../../../resources/styles";
|
||||
import "../../../components/ha-icon-next";
|
||||
import { fireEvent } from "../../../common/dom/fire_event";
|
||||
import { mdiDotsVertical } from "@mdi/js";
|
||||
|
||||
export interface ConfigEntryUpdatedEvent {
|
||||
entry: ConfigEntry;
|
||||
@ -44,6 +45,17 @@ declare global {
|
||||
}
|
||||
}
|
||||
|
||||
const integrationsWithPanel = {
|
||||
zha: {
|
||||
buttonLocalizeKey: "ui.panel.config.zha.button",
|
||||
path: "/config/zha/dashboard",
|
||||
},
|
||||
zwave: {
|
||||
buttonLocalizeKey: "ui.panel.config.zwave.button",
|
||||
path: "/config/zwave",
|
||||
},
|
||||
};
|
||||
|
||||
@customElement("ha-integration-card")
|
||||
export class HaIntegrationCard extends LitElement {
|
||||
@property() public hass!: HomeAssistant;
|
||||
@ -75,7 +87,7 @@ export class HaIntegrationCard extends LitElement {
|
||||
|
||||
private _renderGroupedIntegration(): TemplateResult {
|
||||
return html`
|
||||
<ha-card class="group">
|
||||
<ha-card outlined class="group">
|
||||
<div class="group-header">
|
||||
<img
|
||||
src="https://brands.home-assistant.io/${this.domain}/icon.png"
|
||||
@ -111,6 +123,7 @@ export class HaIntegrationCard extends LitElement {
|
||||
const entities = this._getEntities(item);
|
||||
return html`
|
||||
<ha-card
|
||||
outlined
|
||||
class="single integration"
|
||||
.configEntry=${item}
|
||||
.id=${item.entry_id}
|
||||
@ -178,42 +191,46 @@ export class HaIntegrationCard extends LitElement {
|
||||
"ui.panel.config.integrations.config_entry.rename"
|
||||
)}</mwc-button
|
||||
>
|
||||
${item.supports_options
|
||||
${item.domain in integrationsWithPanel
|
||||
? html`<a
|
||||
href=${`${
|
||||
integrationsWithPanel[item.domain].path
|
||||
}?config_entry=${item.entry_id}`}
|
||||
><mwc-button>
|
||||
${this.hass.localize(
|
||||
integrationsWithPanel[item.domain].buttonLocalizeKey
|
||||
)}
|
||||
</mwc-button></a
|
||||
>`
|
||||
: item.supports_options
|
||||
? html`
|
||||
<mwc-button @click=${this._showOptions}
|
||||
>${this.hass.localize(
|
||||
<mwc-button @click=${this._showOptions}>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.integrations.config_entry.options"
|
||||
)}</mwc-button
|
||||
>
|
||||
)}
|
||||
</mwc-button>
|
||||
`
|
||||
: ""}
|
||||
</div>
|
||||
<paper-menu-button
|
||||
horizontal-align="right"
|
||||
vertical-align="top"
|
||||
vertical-offset="40"
|
||||
close-on-activate
|
||||
>
|
||||
<ha-icon-button
|
||||
icon="hass:dots-vertical"
|
||||
slot="dropdown-trigger"
|
||||
aria-label=${this.hass!.localize(
|
||||
"ui.panel.lovelace.editor.edit_card.options"
|
||||
<ha-button-menu corner="BOTTOM_START">
|
||||
<mwc-icon-button
|
||||
.title=${this.hass.localize("ui.common.menu")}
|
||||
.label=${this.hass.localize("ui.common.overflow_menu")}
|
||||
slot="trigger"
|
||||
>
|
||||
<ha-svg-icon path=${mdiDotsVertical}></ha-svg-icon>
|
||||
</mwc-icon-button>
|
||||
<mwc-list-item @click=${this._showSystemOptions}>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.integrations.config_entry.system_options"
|
||||
)}
|
||||
></ha-icon-button>
|
||||
<paper-listbox slot="dropdown-content">
|
||||
<paper-item @tap=${this._showSystemOptions}>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.integrations.config_entry.system_options"
|
||||
)}</paper-item
|
||||
>
|
||||
<paper-item class="warning" @tap=${this._removeIntegration}>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.integrations.config_entry.delete"
|
||||
)}</paper-item
|
||||
>
|
||||
</paper-listbox>
|
||||
</paper-menu-button>
|
||||
</mwc-list-item>
|
||||
<mwc-list-item class="warning" @click=${this._removeIntegration}>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.integrations.config_entry.delete"
|
||||
)}
|
||||
</mwc-list-item>
|
||||
</ha-button-menu>
|
||||
</div>
|
||||
</ha-card>
|
||||
`;
|
||||
@ -379,9 +396,8 @@ export class HaIntegrationCard extends LitElement {
|
||||
margin-top: 0;
|
||||
min-height: 24px;
|
||||
}
|
||||
paper-menu-button {
|
||||
ha-button-menu {
|
||||
color: var(--secondary-text-color);
|
||||
padding: 0;
|
||||
}
|
||||
@media (min-width: 563px) {
|
||||
paper-listbox {
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { Cluster, ZHADevice, ZHAGroup } from "../../../data/zha";
|
||||
import { Cluster, ZHADevice, ZHAGroup } from "../../../../../data/zha";
|
||||
|
||||
export const formatAsPaddedHex = (value: string | number): string => {
|
||||
let hex = value;
|
||||
@ -8,6 +8,9 @@ export const formatAsPaddedHex = (value: string | number): string => {
|
||||
return "0x" + hex.toString(16).padStart(4, "0");
|
||||
};
|
||||
|
||||
export const getIeeeTail = (ieee: string) =>
|
||||
ieee.split(":").slice(-4).reverse().join("");
|
||||
|
||||
export const sortZHADevices = (a: ZHADevice, b: ZHADevice): number => {
|
||||
const nameA = a.user_given_name ? a.user_given_name : a.name;
|
||||
const nameb = b.user_given_name ? b.user_given_name : b.name;
|
@ -1,4 +1,4 @@
|
||||
import { Cluster, ZHADevice } from "../../../data/zha";
|
||||
import { Cluster, ZHADevice } from "../../../../../data/zha";
|
||||
|
||||
export interface PickerTarget extends EventTarget {
|
||||
selected: number;
|
@ -1,5 +1,5 @@
|
||||
import "@material/mwc-button";
|
||||
import "../../../components/ha-icon-button";
|
||||
import "../../../../../components/ha-icon-button";
|
||||
import "@polymer/paper-spinner/paper-spinner";
|
||||
import {
|
||||
css,
|
||||
@ -9,19 +9,24 @@ import {
|
||||
LitElement,
|
||||
property,
|
||||
TemplateResult,
|
||||
PropertyValues,
|
||||
} from "lit-element";
|
||||
import "../../../components/ha-service-description";
|
||||
import "../../../components/ha-textarea";
|
||||
import { ZHADevice } from "../../../data/zha";
|
||||
import "../../../layouts/hass-subpage";
|
||||
import { haStyle } from "../../../resources/styles";
|
||||
import { HomeAssistant, Route } from "../../../types";
|
||||
import "../../../../../components/ha-service-description";
|
||||
import "@polymer/paper-input/paper-textarea";
|
||||
import { ZHADevice } from "../../../../../data/zha";
|
||||
import "../../../../../layouts/hass-tabs-subpage";
|
||||
import { haStyle } from "../../../../../resources/styles";
|
||||
import { HomeAssistant, Route } from "../../../../../types";
|
||||
import "./zha-device-card";
|
||||
import { zhaTabs } from "./zha-config-dashboard";
|
||||
import { IronAutogrowTextareaElement } from "@polymer/iron-autogrow-textarea";
|
||||
|
||||
@customElement("zha-add-devices-page")
|
||||
class ZHAAddDevicesPage extends LitElement {
|
||||
@property() public hass!: HomeAssistant;
|
||||
|
||||
@property() public narrow?: boolean;
|
||||
|
||||
@property() public isWide?: boolean;
|
||||
|
||||
@property() public route?: Route;
|
||||
@ -36,6 +41,8 @@ class ZHAAddDevicesPage extends LitElement {
|
||||
|
||||
@property() private _showHelp = false;
|
||||
|
||||
@property() private _showLogs = false;
|
||||
|
||||
private _ieeeAddress?: string;
|
||||
|
||||
private _addDevicesTimeoutHandle: any = undefined;
|
||||
@ -60,58 +67,63 @@ class ZHAAddDevicesPage extends LitElement {
|
||||
this._formattedEvents = "";
|
||||
}
|
||||
|
||||
protected updated(changedProps: PropertyValues) {
|
||||
super.updated(changedProps);
|
||||
if (
|
||||
changedProps.has("hass") &&
|
||||
!this._active &&
|
||||
!changedProps.get("hass")
|
||||
) {
|
||||
this._subscribe();
|
||||
}
|
||||
}
|
||||
|
||||
protected render(): TemplateResult {
|
||||
return html`
|
||||
<hass-subpage
|
||||
header="${this.hass!.localize(
|
||||
"ui.panel.config.zha.add_device_page.header"
|
||||
)}"
|
||||
<hass-tabs-subpage
|
||||
.hass=${this.hass}
|
||||
.narrow=${this.narrow}
|
||||
.route=${this.route}
|
||||
.tabs=${zhaTabs}
|
||||
>
|
||||
${this._active
|
||||
? html`
|
||||
<h2>
|
||||
<paper-spinner
|
||||
?active="${this._active}"
|
||||
alt="Searching"
|
||||
></paper-spinner>
|
||||
${this.hass!.localize(
|
||||
"ui.panel.config.zha.add_device_page.spinner"
|
||||
)}
|
||||
</h2>
|
||||
`
|
||||
: html`
|
||||
<div class="card-actions">
|
||||
<mwc-button @click=${this._subscribe} class="search-button">
|
||||
<mwc-button slot="toolbar-icon" @click=${this._toggleLogs}
|
||||
>${this._showLogs ? "Hide logs" : "Show logs"}</mwc-button
|
||||
>
|
||||
<div class="searching">
|
||||
${this._active
|
||||
? html`
|
||||
<h1>
|
||||
${this.hass!.localize(
|
||||
"ui.panel.config.zha.add_device_page.search_again"
|
||||
"ui.panel.config.zha.add_device_page.spinner"
|
||||
)}
|
||||
</mwc-button>
|
||||
<ha-icon-button
|
||||
class="toggle-help-icon"
|
||||
@click="${this._onHelpTap}"
|
||||
icon="hass:help-circle"
|
||||
></ha-icon-button>
|
||||
${this._showHelp
|
||||
? html`
|
||||
<ha-service-description
|
||||
.hass=${this.hass}
|
||||
domain="zha"
|
||||
service="permit"
|
||||
class="help-text"
|
||||
></ha-service-description>
|
||||
`
|
||||
: ""}
|
||||
</div>
|
||||
`}
|
||||
</h1>
|
||||
<paper-spinner active alt="Searching"></paper-spinner>
|
||||
`
|
||||
: html`
|
||||
<div>
|
||||
<mwc-button @click=${this._subscribe} class="search-button">
|
||||
${this.hass!.localize(
|
||||
"ui.panel.config.zha.add_device_page.search_again"
|
||||
)}
|
||||
</mwc-button>
|
||||
</div>
|
||||
`}
|
||||
</div>
|
||||
${this._error ? html` <div class="error">${this._error}</div> ` : ""}
|
||||
<div class="content-header"></div>
|
||||
<div class="content">
|
||||
${this._discoveredDevices.length < 1
|
||||
? html`
|
||||
<div class="discovery-text">
|
||||
<h4>
|
||||
${this.hass!.localize(
|
||||
"ui.panel.config.zha.add_device_page.discovery_text"
|
||||
"ui.panel.config.zha.add_device_page.pairing_mode"
|
||||
)}
|
||||
</h4>
|
||||
<h4>
|
||||
${this.hass!.localize(
|
||||
this._active
|
||||
? "ui.panel.config.zha.add_device_page.discovered_text"
|
||||
: "ui.panel.config.zha.add_device_page.no_devices_found"
|
||||
)}
|
||||
</h4>
|
||||
</div>
|
||||
@ -123,27 +135,38 @@ class ZHAAddDevicesPage extends LitElement {
|
||||
class="card"
|
||||
.hass=${this.hass}
|
||||
.device=${device}
|
||||
.narrow=${!this.isWide}
|
||||
.narrow=${this.narrow}
|
||||
.showHelp=${this._showHelp}
|
||||
.showActions=${!this._active}
|
||||
.showEntityDetail=${false}
|
||||
></zha-device-card>
|
||||
`
|
||||
)}
|
||||
`}
|
||||
</div>
|
||||
<ha-textarea class="events" value="${this._formattedEvents}">
|
||||
</ha-textarea>
|
||||
</hass-subpage>
|
||||
${this._showLogs
|
||||
? html`<paper-textarea
|
||||
readonly
|
||||
max-rows="10"
|
||||
class="log"
|
||||
value="${this._formattedEvents}"
|
||||
>
|
||||
</paper-textarea>`
|
||||
: ""}
|
||||
</hass-tabs-subpage>
|
||||
`;
|
||||
}
|
||||
|
||||
private _toggleLogs() {
|
||||
this._showLogs = !this._showLogs;
|
||||
}
|
||||
|
||||
private _handleMessage(message: any): void {
|
||||
if (message.type === "log_output") {
|
||||
this._formattedEvents += message.log_entry.message + "\n";
|
||||
if (this.shadowRoot) {
|
||||
const textArea = this.shadowRoot.querySelector("ha-textarea");
|
||||
if (textArea) {
|
||||
const paperTextArea = this.shadowRoot.querySelector("paper-textarea");
|
||||
if (paperTextArea) {
|
||||
const textArea = (paperTextArea.inputElement as IronAutogrowTextareaElement)
|
||||
.textarea;
|
||||
textArea.scrollTop = textArea.scrollHeight;
|
||||
}
|
||||
}
|
||||
@ -165,69 +188,58 @@ class ZHAAddDevicesPage extends LitElement {
|
||||
}
|
||||
|
||||
private _subscribe(): void {
|
||||
if (!this.hass) {
|
||||
return;
|
||||
}
|
||||
this._active = true;
|
||||
const data: any = { type: "zha/devices/permit" };
|
||||
if (this._ieeeAddress) {
|
||||
data.ieee = this._ieeeAddress;
|
||||
}
|
||||
this._subscribed = this.hass!.connection.subscribeMessage(
|
||||
this._subscribed = this.hass.connection.subscribeMessage(
|
||||
(message) => this._handleMessage(message),
|
||||
data
|
||||
);
|
||||
this._active = true;
|
||||
this._addDevicesTimeoutHandle = setTimeout(
|
||||
() => this._unsubscribe(),
|
||||
120000
|
||||
);
|
||||
}
|
||||
|
||||
private _onHelpTap(): void {
|
||||
this._showHelp = !this._showHelp;
|
||||
}
|
||||
|
||||
static get styles(): CSSResult[] {
|
||||
return [
|
||||
haStyle,
|
||||
css`
|
||||
.discovery-text,
|
||||
.content-header {
|
||||
margin: 16px;
|
||||
.discovery-text {
|
||||
width: 100%;
|
||||
padding: 16px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
.content {
|
||||
border-top: 1px solid var(--light-primary-color);
|
||||
min-height: 500px;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
padding: 4px;
|
||||
justify-content: left;
|
||||
overflow: scroll;
|
||||
justify-content: center;
|
||||
}
|
||||
.error {
|
||||
color: var(--google-red-500);
|
||||
}
|
||||
paper-spinner {
|
||||
display: none;
|
||||
margin-right: 20px;
|
||||
margin-left: 16px;
|
||||
padding: 20px;
|
||||
}
|
||||
paper-spinner[active] {
|
||||
display: block;
|
||||
float: left;
|
||||
margin-right: 20px;
|
||||
margin-left: 16px;
|
||||
.searching {
|
||||
margin-top: 20px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
.card {
|
||||
margin-left: 16px;
|
||||
margin-right: 16px;
|
||||
margin-bottom: 0px;
|
||||
margin-top: 10px;
|
||||
margin: 8px;
|
||||
}
|
||||
.events {
|
||||
margin: 16px;
|
||||
border-top: 1px solid var(--light-primary-color);
|
||||
padding-top: 16px;
|
||||
min-height: 200px;
|
||||
max-height: 200px;
|
||||
overflow: scroll;
|
||||
.log {
|
||||
padding: 16px;
|
||||
}
|
||||
.toggle-help-icon {
|
||||
position: absolute;
|
@ -12,20 +12,20 @@ import {
|
||||
PropertyValues,
|
||||
query,
|
||||
} from "lit-element";
|
||||
import type { HASSDomEvent } from "../../../common/dom/fire_event";
|
||||
import { navigate } from "../../../common/navigate";
|
||||
import type { SelectionChangedEvent } from "../../../components/data-table/ha-data-table";
|
||||
import type { HASSDomEvent } from "../../../../../common/dom/fire_event";
|
||||
import { navigate } from "../../../../../common/navigate";
|
||||
import type { SelectionChangedEvent } from "../../../../../components/data-table/ha-data-table";
|
||||
import {
|
||||
addGroup,
|
||||
fetchGroupableDevices,
|
||||
ZHAGroup,
|
||||
ZHADeviceEndpoint,
|
||||
} from "../../../data/zha";
|
||||
import "../../../layouts/hass-error-screen";
|
||||
import "../../../layouts/hass-subpage";
|
||||
import type { PolymerChangedEvent } from "../../../polymer-types";
|
||||
import type { HomeAssistant } from "../../../types";
|
||||
import "../ha-config-section";
|
||||
} from "../../../../../data/zha";
|
||||
import "../../../../../layouts/hass-error-screen";
|
||||
import "../../../../../layouts/hass-subpage";
|
||||
import type { PolymerChangedEvent } from "../../../../../polymer-types";
|
||||
import type { HomeAssistant } from "../../../../../types";
|
||||
import "../../../ha-config-section";
|
||||
import "./zha-device-endpoint-data-table";
|
||||
import type { ZHADeviceEndpointDataTable } from "./zha-device-endpoint-data-table";
|
||||
|
@ -1,6 +1,6 @@
|
||||
import "@material/mwc-button";
|
||||
import "@polymer/paper-dropdown-menu/paper-dropdown-menu";
|
||||
import "../../../components/ha-icon-button";
|
||||
import "../../../../../components/ha-icon-button";
|
||||
import "@polymer/paper-input/paper-input";
|
||||
import "@polymer/paper-item/paper-item";
|
||||
import "@polymer/paper-listbox/paper-listbox";
|
||||
@ -13,9 +13,9 @@ import {
|
||||
PropertyValues,
|
||||
TemplateResult,
|
||||
} from "lit-element";
|
||||
import "../../../components/buttons/ha-call-service-button";
|
||||
import "../../../components/ha-card";
|
||||
import "../../../components/ha-service-description";
|
||||
import "../../../../../components/buttons/ha-call-service-button";
|
||||
import "../../../../../components/ha-card";
|
||||
import "../../../../../components/ha-service-description";
|
||||
import {
|
||||
Attribute,
|
||||
Cluster,
|
||||
@ -23,10 +23,10 @@ import {
|
||||
ReadAttributeServiceData,
|
||||
readAttributeValue,
|
||||
ZHADevice,
|
||||
} from "../../../data/zha";
|
||||
import { haStyle } from "../../../resources/styles";
|
||||
import { HomeAssistant } from "../../../types";
|
||||
import "../ha-config-section";
|
||||
} from "../../../../../data/zha";
|
||||
import { haStyle } from "../../../../../resources/styles";
|
||||
import { HomeAssistant } from "../../../../../types";
|
||||
import "../../../ha-config-section";
|
||||
import { formatAsPaddedHex } from "./functions";
|
||||
import {
|
||||
ChangeEvent,
|
@ -1,5 +1,5 @@
|
||||
import "@polymer/paper-dropdown-menu/paper-dropdown-menu";
|
||||
import "../../../components/ha-icon-button";
|
||||
import "../../../../../components/ha-icon-button";
|
||||
import "@polymer/paper-input/paper-input";
|
||||
import "@polymer/paper-item/paper-item";
|
||||
import "@polymer/paper-listbox/paper-listbox";
|
||||
@ -12,18 +12,18 @@ import {
|
||||
PropertyValues,
|
||||
TemplateResult,
|
||||
} from "lit-element";
|
||||
import "../../../components/buttons/ha-call-service-button";
|
||||
import "../../../components/ha-card";
|
||||
import "../../../components/ha-service-description";
|
||||
import "../../../../../components/buttons/ha-call-service-button";
|
||||
import "../../../../../components/ha-card";
|
||||
import "../../../../../components/ha-service-description";
|
||||
import {
|
||||
Cluster,
|
||||
Command,
|
||||
fetchCommandsForCluster,
|
||||
ZHADevice,
|
||||
} from "../../../data/zha";
|
||||
import { haStyle } from "../../../resources/styles";
|
||||
import { HomeAssistant } from "../../../types";
|
||||
import "../ha-config-section";
|
||||
} from "../../../../../data/zha";
|
||||
import { haStyle } from "../../../../../resources/styles";
|
||||
import { HomeAssistant } from "../../../../../types";
|
||||
import "../../../ha-config-section";
|
||||
import { formatAsPaddedHex } from "./functions";
|
||||
import {
|
||||
ChangeEvent,
|
@ -7,14 +7,14 @@ import {
|
||||
TemplateResult,
|
||||
} from "lit-element";
|
||||
import memoizeOne from "memoize-one";
|
||||
import "../../../components/data-table/ha-data-table";
|
||||
import "../../../../../components/data-table/ha-data-table";
|
||||
import type {
|
||||
DataTableColumnContainer,
|
||||
HaDataTable,
|
||||
} from "../../../components/data-table/ha-data-table";
|
||||
import "../../../components/entity/ha-state-icon";
|
||||
import type { Cluster } from "../../../data/zha";
|
||||
import type { HomeAssistant } from "../../../types";
|
||||
} from "../../../../../components/data-table/ha-data-table";
|
||||
import "../../../../../components/entity/ha-state-icon";
|
||||
import type { Cluster } from "../../../../../data/zha";
|
||||
import type { HomeAssistant } from "../../../../../types";
|
||||
import { formatAsPaddedHex } from "./functions";
|
||||
|
||||
export interface ClusterRowData extends Cluster {
|
@ -1,5 +1,5 @@
|
||||
import "@polymer/paper-dropdown-menu/paper-dropdown-menu";
|
||||
import "../../../components/ha-icon-button";
|
||||
import "../../../../../components/ha-icon-button";
|
||||
import "@polymer/paper-item/paper-item";
|
||||
import "@polymer/paper-listbox/paper-listbox";
|
||||
import {
|
||||
@ -11,14 +11,18 @@ import {
|
||||
PropertyValues,
|
||||
TemplateResult,
|
||||
} from "lit-element";
|
||||
import { fireEvent } from "../../../common/dom/fire_event";
|
||||
import "../../../components/buttons/ha-call-service-button";
|
||||
import "../../../components/ha-card";
|
||||
import "../../../components/ha-service-description";
|
||||
import { Cluster, fetchClustersForZhaNode, ZHADevice } from "../../../data/zha";
|
||||
import { haStyle } from "../../../resources/styles";
|
||||
import { HomeAssistant } from "../../../types";
|
||||
import "../ha-config-section";
|
||||
import { fireEvent } from "../../../../../common/dom/fire_event";
|
||||
import "../../../../../components/buttons/ha-call-service-button";
|
||||
import "../../../../../components/ha-card";
|
||||
import "../../../../../components/ha-service-description";
|
||||
import {
|
||||
Cluster,
|
||||
fetchClustersForZhaNode,
|
||||
ZHADevice,
|
||||
} from "../../../../../data/zha";
|
||||
import { haStyle } from "../../../../../resources/styles";
|
||||
import { HomeAssistant } from "../../../../../types";
|
||||
import "../../../ha-config-section";
|
||||
import { computeClusterKey } from "./functions";
|
||||
import { ItemSelectedEvent } from "./types";
|
||||
|
@ -2,8 +2,9 @@ import { customElement, property } from "lit-element";
|
||||
import {
|
||||
HassRouterPage,
|
||||
RouterOptions,
|
||||
} from "../../../layouts/hass-router-page";
|
||||
import { HomeAssistant } from "../../../types";
|
||||
} from "../../../../../layouts/hass-router-page";
|
||||
import { HomeAssistant } from "../../../../../types";
|
||||
import { navigate } from "../../../../../common/navigate";
|
||||
|
||||
@customElement("zha-config-dashboard-router")
|
||||
class ZHAConfigDashboardRouter extends HassRouterPage {
|
||||
@ -13,6 +14,10 @@ class ZHAConfigDashboardRouter extends HassRouterPage {
|
||||
|
||||
@property() public narrow!: boolean;
|
||||
|
||||
private _configEntry = new URLSearchParams(window.location.search).get(
|
||||
"config_entry"
|
||||
);
|
||||
|
||||
protected routerOptions: RouterOptions = {
|
||||
defaultPage: "dashboard",
|
||||
showLoading: true,
|
||||
@ -24,13 +29,6 @@ class ZHAConfigDashboardRouter extends HassRouterPage {
|
||||
/* webpackChunkName: "zha-config-dashboard" */ "./zha-config-dashboard"
|
||||
),
|
||||
},
|
||||
device: {
|
||||
tag: "zha-device-page",
|
||||
load: () =>
|
||||
import(
|
||||
/* webpackChunkName: "zha-devices-page" */ "./zha-device-page"
|
||||
),
|
||||
},
|
||||
add: {
|
||||
tag: "zha-add-devices-page",
|
||||
load: () =>
|
||||
@ -65,11 +63,24 @@ class ZHAConfigDashboardRouter extends HassRouterPage {
|
||||
el.hass = this.hass;
|
||||
el.isWide = this.isWide;
|
||||
el.narrow = this.narrow;
|
||||
el.configEntryId = this._configEntry;
|
||||
if (this._currentPage === "group") {
|
||||
el.groupId = this.routeTail.path.substr(1);
|
||||
} else if (this._currentPage === "device") {
|
||||
el.ieee = this.routeTail.path.substr(1);
|
||||
}
|
||||
|
||||
const searchParams = new URLSearchParams(window.location.search);
|
||||
if (this._configEntry && !searchParams.has("config_entry")) {
|
||||
searchParams.append("config_entry", this._configEntry);
|
||||
navigate(
|
||||
this,
|
||||
`${this.routeTail.prefix}${
|
||||
this.routeTail.path
|
||||
}?${searchParams.toString()}`,
|
||||
true
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,132 @@
|
||||
import "@polymer/paper-item/paper-item";
|
||||
import "@polymer/paper-item/paper-item-body";
|
||||
import "@material/mwc-fab";
|
||||
import {
|
||||
css,
|
||||
CSSResultArray,
|
||||
customElement,
|
||||
html,
|
||||
LitElement,
|
||||
property,
|
||||
TemplateResult,
|
||||
} from "lit-element";
|
||||
import "../../../../../components/ha-card";
|
||||
import "../../../../../components/ha-icon-next";
|
||||
import { haStyle } from "../../../../../resources/styles";
|
||||
import type { HomeAssistant, Route } from "../../../../../types";
|
||||
import "../../../ha-config-section";
|
||||
import { mdiNetwork, mdiFolderMultipleOutline, mdiPlus } from "@mdi/js";
|
||||
import "../../../../../layouts/hass-tabs-subpage";
|
||||
import type { PageNavigation } from "../../../../../layouts/hass-tabs-subpage";
|
||||
import { computeRTL } from "../../../../../common/util/compute_rtl";
|
||||
|
||||
export const zhaTabs: PageNavigation[] = [
|
||||
{
|
||||
translationKey: "ui.panel.config.zha.network.caption",
|
||||
path: `/config/zha/dashboard`,
|
||||
iconPath: mdiNetwork,
|
||||
},
|
||||
{
|
||||
translationKey: "ui.panel.config.zha.groups.caption",
|
||||
path: `/config/zha/groups`,
|
||||
iconPath: mdiFolderMultipleOutline,
|
||||
},
|
||||
];
|
||||
|
||||
@customElement("zha-config-dashboard")
|
||||
class ZHAConfigDashboard extends LitElement {
|
||||
@property({ type: Object }) public hass!: HomeAssistant;
|
||||
|
||||
@property({ type: Object }) public route!: Route;
|
||||
|
||||
@property({ type: Boolean }) public narrow!: boolean;
|
||||
|
||||
@property({ type: Boolean }) public isWide!: boolean;
|
||||
|
||||
@property() public configEntryId?: string;
|
||||
|
||||
protected render(): TemplateResult {
|
||||
return html`
|
||||
<hass-tabs-subpage
|
||||
.hass=${this.hass}
|
||||
.narrow=${this.narrow}
|
||||
.route=${this.route}
|
||||
.tabs=${zhaTabs}
|
||||
back-path="/config/integrations"
|
||||
>
|
||||
<ha-card header="Zigbee Network">
|
||||
<div class="card-content">
|
||||
Network info/settings for specific config entry
|
||||
</div>
|
||||
${this.configEntryId
|
||||
? html`<div class="card-actions">
|
||||
<a
|
||||
href="${`/config/devices/dashboard?historyBack=1&config_entry=${this.configEntryId}`}"
|
||||
>
|
||||
<mwc-button>Devices</mwc-button>
|
||||
</a>
|
||||
<a
|
||||
href="${`/config/entities/dashboard?historyBack=1&config_entry=${this.configEntryId}`}"
|
||||
>
|
||||
<mwc-button>Entities</mwc-button>
|
||||
</a>
|
||||
</div>`
|
||||
: ""}
|
||||
</ha-card>
|
||||
<a href="/config/zha/add">
|
||||
<mwc-fab
|
||||
?is-wide=${this.isWide}
|
||||
?narrow=${this.narrow}
|
||||
title=${this.hass.localize("ui.panel.config.zha.add_device")}
|
||||
?rtl=${computeRTL(this.hass)}
|
||||
>
|
||||
<ha-svg-icon slot="icon" path=${mdiPlus}></ha-svg-icon>
|
||||
</mwc-fab>
|
||||
</a>
|
||||
</hass-tabs-subpage>
|
||||
`;
|
||||
}
|
||||
|
||||
static get styles(): CSSResultArray {
|
||||
return [
|
||||
haStyle,
|
||||
css`
|
||||
ha-card {
|
||||
margin: auto;
|
||||
margin-top: 16px;
|
||||
max-width: 500px;
|
||||
}
|
||||
mwc-fab {
|
||||
position: fixed;
|
||||
bottom: 16px;
|
||||
right: 16px;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
mwc-fab[is-wide] {
|
||||
bottom: 24px;
|
||||
right: 24px;
|
||||
}
|
||||
mwc-fab[narrow] {
|
||||
bottom: 84px;
|
||||
}
|
||||
mwc-fab[rtl] {
|
||||
right: auto;
|
||||
left: 16px;
|
||||
}
|
||||
|
||||
mwc-fab[rtl][is-wide] {
|
||||
bottom: 24px;
|
||||
right: auto;
|
||||
left: 24px;
|
||||
}
|
||||
`,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"zha-config-dashboard": ZHAConfigDashboard;
|
||||
}
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
import "@material/mwc-button/mwc-button";
|
||||
import "@polymer/paper-dropdown-menu/paper-dropdown-menu";
|
||||
import "../../../components/ha-icon-button";
|
||||
import "../../../../../components/ha-icon-button";
|
||||
import "@polymer/paper-item/paper-item";
|
||||
import "@polymer/paper-listbox/paper-listbox";
|
||||
import {
|
||||
@ -13,13 +13,13 @@ import {
|
||||
PropertyValues,
|
||||
TemplateResult,
|
||||
} from "lit-element";
|
||||
import "../../../components/buttons/ha-call-service-button";
|
||||
import "../../../components/ha-card";
|
||||
import "../../../components/ha-service-description";
|
||||
import { bindDevices, unbindDevices, ZHADevice } from "../../../data/zha";
|
||||
import { haStyle } from "../../../resources/styles";
|
||||
import { HomeAssistant } from "../../../types";
|
||||
import "../ha-config-section";
|
||||
import "../../../../../components/buttons/ha-call-service-button";
|
||||
import "../../../../../components/ha-card";
|
||||
import "../../../../../components/ha-service-description";
|
||||
import { bindDevices, unbindDevices, ZHADevice } from "../../../../../data/zha";
|
||||
import { haStyle } from "../../../../../resources/styles";
|
||||
import { HomeAssistant } from "../../../../../types";
|
||||
import "../../../ha-config-section";
|
||||
import { ItemSelectedEvent } from "./types";
|
||||
|
||||
@customElement("zha-device-binding-control")
|
@ -0,0 +1,243 @@
|
||||
import "@polymer/paper-input/paper-input";
|
||||
import "@polymer/paper-listbox/paper-listbox";
|
||||
import { UnsubscribeFunc } from "home-assistant-js-websocket";
|
||||
import {
|
||||
css,
|
||||
CSSResult,
|
||||
customElement,
|
||||
html,
|
||||
LitElement,
|
||||
property,
|
||||
TemplateResult,
|
||||
} from "lit-element";
|
||||
import { fireEvent } from "../../../../../common/dom/fire_event";
|
||||
import { computeStateName } from "../../../../../common/entity/compute_state_name";
|
||||
import "../../../../../components/buttons/ha-call-service-button";
|
||||
import "../../../../../components/entity/state-badge";
|
||||
import "../../../../../components/ha-card";
|
||||
import "../../../../../components/ha-service-description";
|
||||
import { updateDeviceRegistryEntry } from "../../../../../data/device_registry";
|
||||
import { ZHADevice } from "../../../../../data/zha";
|
||||
import { haStyle } from "../../../../../resources/styles";
|
||||
import { HomeAssistant } from "../../../../../types";
|
||||
import "../../../../../components/ha-area-picker";
|
||||
import { showAlertDialog } from "../../../../../dialogs/generic/show-dialog-box";
|
||||
import { SubscribeMixin } from "../../../../../mixins/subscribe-mixin";
|
||||
import {
|
||||
subscribeEntityRegistry,
|
||||
EntityRegistryEntry,
|
||||
updateEntityRegistryEntry,
|
||||
} from "../../../../../data/entity_registry";
|
||||
import { createValidEntityId } from "../../../../../common/entity/valid_entity_id";
|
||||
import memoizeOne from "memoize-one";
|
||||
import { EntityRegistryStateEntry } from "../../../devices/ha-config-device-page";
|
||||
import { compare } from "../../../../../common/string/compare";
|
||||
import { getIeeeTail } from "./functions";
|
||||
|
||||
@customElement("zha-device-card")
|
||||
class ZHADeviceCard extends SubscribeMixin(LitElement) {
|
||||
@property() public hass!: HomeAssistant;
|
||||
|
||||
@property() public device?: ZHADevice;
|
||||
|
||||
@property({ type: Boolean }) public narrow?: boolean;
|
||||
|
||||
@property() private _entities: EntityRegistryEntry[] = [];
|
||||
|
||||
private _deviceEntities = memoizeOne(
|
||||
(
|
||||
deviceId: string,
|
||||
entities: EntityRegistryEntry[]
|
||||
): EntityRegistryStateEntry[] =>
|
||||
entities
|
||||
.filter((entity) => entity.device_id === deviceId)
|
||||
.map((entity) => {
|
||||
return { ...entity, stateName: this._computeEntityName(entity) };
|
||||
})
|
||||
.sort((ent1, ent2) =>
|
||||
compare(
|
||||
ent1.stateName || `zzz${ent1.entity_id}`,
|
||||
ent2.stateName || `zzz${ent2.entity_id}`
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
public hassSubscribe(): UnsubscribeFunc[] {
|
||||
return [
|
||||
subscribeEntityRegistry(this.hass.connection, (entities) => {
|
||||
this._entities = entities;
|
||||
}),
|
||||
];
|
||||
}
|
||||
|
||||
protected render(): TemplateResult {
|
||||
if (!this.hass || !this.device) {
|
||||
return html``;
|
||||
}
|
||||
const entities = this._deviceEntities(
|
||||
this.device.device_reg_id,
|
||||
this._entities
|
||||
);
|
||||
|
||||
return html`
|
||||
<ha-card .header=${this.device.user_given_name || this.device.name}>
|
||||
<div class="card-content">
|
||||
<div class="info">
|
||||
<div class="model">${this.device.model}</div>
|
||||
<div class="manuf">
|
||||
${this.hass.localize(
|
||||
"ui.dialogs.zha_device_info.manuf",
|
||||
"manufacturer",
|
||||
this.device.manufacturer
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="device-entities">
|
||||
${entities.map(
|
||||
(entity) => html`
|
||||
<state-badge
|
||||
@click="${this._openMoreInfo}"
|
||||
.title=${entity.stateName!}
|
||||
.stateObj="${this.hass!.states[entity.entity_id]}"
|
||||
slot="item-icon"
|
||||
></state-badge>
|
||||
`
|
||||
)}
|
||||
</div>
|
||||
<paper-input
|
||||
type="string"
|
||||
@change=${this._rename}
|
||||
.value=${this.device.user_given_name || this.device.name}
|
||||
.label=${this.hass.localize(
|
||||
"ui.dialogs.zha_device_info.zha_device_card.device_name_placeholder"
|
||||
)}
|
||||
></paper-input>
|
||||
<ha-area-picker
|
||||
.hass=${this.hass}
|
||||
.device=${this.device.device_reg_id}
|
||||
@value-changed=${this._areaPicked}
|
||||
></ha-area-picker>
|
||||
</div>
|
||||
</ha-card>
|
||||
`;
|
||||
}
|
||||
|
||||
private async _rename(event): Promise<void> {
|
||||
if (!this.hass || !this.device) {
|
||||
return;
|
||||
}
|
||||
const device = this.device;
|
||||
|
||||
const oldDeviceName = device.user_given_name || device.name;
|
||||
const newDeviceName = event.target.value;
|
||||
this.device.user_given_name = newDeviceName;
|
||||
await updateDeviceRegistryEntry(this.hass, device.device_reg_id, {
|
||||
name_by_user: newDeviceName,
|
||||
});
|
||||
|
||||
if (!oldDeviceName || !newDeviceName || oldDeviceName === newDeviceName) {
|
||||
return;
|
||||
}
|
||||
const entities = this._deviceEntities(device.device_reg_id, this._entities);
|
||||
|
||||
const oldDeviceEntityId = createValidEntityId(oldDeviceName);
|
||||
const newDeviceEntityId = createValidEntityId(newDeviceName);
|
||||
const ieeeTail = getIeeeTail(device.ieee);
|
||||
|
||||
const updateProms = entities.map((entity) => {
|
||||
const name = entity.name || entity.stateName;
|
||||
let newEntityId: string | null = null;
|
||||
let newName: string | null = null;
|
||||
|
||||
if (name && name.includes(oldDeviceName)) {
|
||||
newName = name.replace(` ${ieeeTail}`, "");
|
||||
newName = newName.replace(oldDeviceName, newDeviceName);
|
||||
newEntityId = entity.entity_id.replace(`_${ieeeTail}`, "");
|
||||
newEntityId = newEntityId.replace(oldDeviceEntityId, newDeviceEntityId);
|
||||
}
|
||||
|
||||
if (!newName && !newEntityId) {
|
||||
return new Promise((resolve) => resolve());
|
||||
}
|
||||
|
||||
return updateEntityRegistryEntry(this.hass!, entity.entity_id, {
|
||||
name: newName || name,
|
||||
disabled_by: entity.disabled_by,
|
||||
new_entity_id: newEntityId || entity.entity_id,
|
||||
});
|
||||
});
|
||||
await Promise.all(updateProms);
|
||||
}
|
||||
|
||||
private _openMoreInfo(ev: MouseEvent): void {
|
||||
fireEvent(this, "hass-more-info", {
|
||||
entityId: (ev.currentTarget as any).stateObj.entity_id,
|
||||
});
|
||||
}
|
||||
|
||||
private _computeEntityName(entity: EntityRegistryEntry): string {
|
||||
if (this.hass.states[entity.entity_id]) {
|
||||
return computeStateName(this.hass.states[entity.entity_id]);
|
||||
}
|
||||
return entity.name;
|
||||
}
|
||||
|
||||
private async _areaPicked(ev: CustomEvent) {
|
||||
const picker = ev.currentTarget as any;
|
||||
|
||||
const area = ev.detail.value;
|
||||
try {
|
||||
await updateDeviceRegistryEntry(this.hass, this.device!.device_reg_id, {
|
||||
area_id: area,
|
||||
});
|
||||
this.device!.area_id = area;
|
||||
} catch (err) {
|
||||
showAlertDialog(this, {
|
||||
text: this.hass.localize(
|
||||
"ui.panel.config.integrations.config_flow.error_saving_area",
|
||||
"error",
|
||||
err.message
|
||||
),
|
||||
});
|
||||
picker.value = null;
|
||||
}
|
||||
}
|
||||
|
||||
static get styles(): CSSResult[] {
|
||||
return [
|
||||
haStyle,
|
||||
css`
|
||||
.device-entities {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
padding: 4px;
|
||||
justify-content: left;
|
||||
min-height: 48px;
|
||||
}
|
||||
.device {
|
||||
width: 30%;
|
||||
}
|
||||
.device .name {
|
||||
font-weight: bold;
|
||||
}
|
||||
.device .manuf {
|
||||
color: var(--secondary-text-color);
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.extra-info {
|
||||
margin-top: 8px;
|
||||
}
|
||||
state-badge {
|
||||
cursor: pointer;
|
||||
}
|
||||
`,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"zha-device-card": ZHADeviceCard;
|
||||
}
|
||||
}
|
@ -9,16 +9,18 @@ import {
|
||||
CSSResult,
|
||||
} from "lit-element";
|
||||
import memoizeOne from "memoize-one";
|
||||
import "../../../components/data-table/ha-data-table";
|
||||
import "../../../../../components/data-table/ha-data-table";
|
||||
import type {
|
||||
DataTableColumnContainer,
|
||||
HaDataTable,
|
||||
DataTableRowData,
|
||||
} from "../../../components/data-table/ha-data-table";
|
||||
import "../../../components/entity/ha-state-icon";
|
||||
import type { ZHADeviceEndpoint, ZHAEntityReference } from "../../../data/zha";
|
||||
import { showZHADeviceInfoDialog } from "../../../dialogs/zha-device-info-dialog/show-dialog-zha-device-info";
|
||||
import type { HomeAssistant } from "../../../types";
|
||||
} from "../../../../../components/data-table/ha-data-table";
|
||||
import "../../../../../components/entity/ha-state-icon";
|
||||
import type {
|
||||
ZHADeviceEndpoint,
|
||||
ZHAEntityReference,
|
||||
} from "../../../../../data/zha";
|
||||
import type { HomeAssistant } from "../../../../../types";
|
||||
|
||||
export interface DeviceEndpointRowData extends DataTableRowData {
|
||||
id: string;
|
||||
@ -55,6 +57,7 @@ export class ZHADeviceEndpointDataTable extends LitElement {
|
||||
ieee: deviceEndpoint.device.ieee,
|
||||
endpoint_id: deviceEndpoint.endpoint_id,
|
||||
entities: deviceEndpoint.entities,
|
||||
dev_id: deviceEndpoint.device.device_reg_id,
|
||||
});
|
||||
});
|
||||
|
||||
@ -72,14 +75,10 @@ export class ZHADeviceEndpointDataTable extends LitElement {
|
||||
filterable: true,
|
||||
direction: "asc",
|
||||
grows: true,
|
||||
template: (name) => html`
|
||||
<div
|
||||
class="mdc-data-table__cell table-cell-text"
|
||||
@click=${this._handleClicked}
|
||||
style="cursor: pointer;"
|
||||
>
|
||||
template: (name, device: any) => html`
|
||||
<a href="${`/config/devices/device/${device.dev_id}`}">
|
||||
${name}
|
||||
</div>
|
||||
</a>
|
||||
`,
|
||||
},
|
||||
endpoint_id: {
|
||||
@ -95,14 +94,10 @@ export class ZHADeviceEndpointDataTable extends LitElement {
|
||||
filterable: true,
|
||||
direction: "asc",
|
||||
grows: true,
|
||||
template: (name) => html`
|
||||
<div
|
||||
class="mdc-data-table__cell table-cell-text"
|
||||
@click=${this._handleClicked}
|
||||
style="cursor: pointer;"
|
||||
>
|
||||
template: (name, device: any) => html`
|
||||
<a href="${`/config/devices/device/${device.dev_id}`}">
|
||||
${name}
|
||||
</div>
|
||||
</a>
|
||||
`,
|
||||
},
|
||||
endpoint_id: {
|
||||
@ -156,14 +151,6 @@ export class ZHADeviceEndpointDataTable extends LitElement {
|
||||
`;
|
||||
}
|
||||
|
||||
private async _handleClicked(ev: CustomEvent) {
|
||||
const rowId = ((ev.target as HTMLElement).closest(
|
||||
".mdc-data-table__row"
|
||||
) as any).rowId;
|
||||
const ieee = rowId.substring(0, rowId.indexOf("_"));
|
||||
showZHADeviceInfoDialog(this, { ieee });
|
||||
}
|
||||
|
||||
static get styles(): CSSResult[] {
|
||||
return [
|
||||
css`
|
@ -1,6 +1,6 @@
|
||||
import "@material/mwc-button/mwc-button";
|
||||
import "@polymer/paper-dropdown-menu/paper-dropdown-menu";
|
||||
import "../../../components/ha-icon-button";
|
||||
import "../../../../../components/ha-icon-button";
|
||||
import "@polymer/paper-item/paper-item";
|
||||
import "@polymer/paper-listbox/paper-listbox";
|
||||
import {
|
||||
@ -14,11 +14,11 @@ import {
|
||||
query,
|
||||
TemplateResult,
|
||||
} from "lit-element";
|
||||
import type { HASSDomEvent } from "../../../common/dom/fire_event";
|
||||
import "../../../components/buttons/ha-call-service-button";
|
||||
import { SelectionChangedEvent } from "../../../components/data-table/ha-data-table";
|
||||
import "../../../components/ha-card";
|
||||
import "../../../components/ha-service-description";
|
||||
import type { HASSDomEvent } from "../../../../../common/dom/fire_event";
|
||||
import "../../../../../components/buttons/ha-call-service-button";
|
||||
import { SelectionChangedEvent } from "../../../../../components/data-table/ha-data-table";
|
||||
import "../../../../../components/ha-card";
|
||||
import "../../../../../components/ha-service-description";
|
||||
import {
|
||||
bindDeviceToGroup,
|
||||
Cluster,
|
||||
@ -26,10 +26,10 @@ import {
|
||||
unbindDeviceFromGroup,
|
||||
ZHADevice,
|
||||
ZHAGroup,
|
||||
} from "../../../data/zha";
|
||||
import { haStyle } from "../../../resources/styles";
|
||||
import type { HomeAssistant } from "../../../types";
|
||||
import "../ha-config-section";
|
||||
} from "../../../../../data/zha";
|
||||
import { haStyle } from "../../../../../resources/styles";
|
||||
import type { HomeAssistant } from "../../../../../types";
|
||||
import "../../../ha-config-section";
|
||||
import { ItemSelectedEvent } from "./types";
|
||||
import "./zha-clusters-data-table";
|
||||
import type { ZHAClustersDataTable } from "./zha-clusters-data-table";
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user