From 18fc0d0342975f60f498d32b5455bde282864dc0 Mon Sep 17 00:00:00 2001 From: yosilevy <37745463+yosilevy@users.noreply.github.com> Date: Mon, 4 Feb 2019 07:24:29 +0200 Subject: [PATCH 01/29] Added forgotten label localization (#2672) --- src/panels/config/config-entries/ha-device-card.js | 4 +++- src/panels/lovelace/components/hui-card-options.ts | 6 +++++- src/translations/en.json | 6 ++++-- 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/src/panels/config/config-entries/ha-device-card.js b/src/panels/config/config-entries/ha-device-card.js index edd367daf5..0bc4265621 100644 --- a/src/panels/config/config-entries/ha-device-card.js +++ b/src/panels/config/config-entries/ha-device-card.js @@ -81,7 +81,9 @@ class HaDeviceCard extends EventsMixin(LocalizeMixin(PolymerElement)) { slot="dropdown-content" selected="[[_computeSelectedArea(areas, device)]]" > - No Area + + [[localize('ui.panel.config.integrations.config_entry.no_area')]] + diff --git a/src/panels/lovelace/components/hui-card-options.ts b/src/panels/lovelace/components/hui-card-options.ts index eddafa4486..52ed479310 100644 --- a/src/panels/lovelace/components/hui-card-options.ts +++ b/src/panels/lovelace/components/hui-card-options.ts @@ -98,7 +98,11 @@ export class HuiCardOptions extends LitElement { slot="dropdown-trigger" > - Move Card + ${this.hass!.localize( + "ui.panel.lovelace.editor.edit_card.move" + )} ${this.hass!.localize( "ui.panel.lovelace.editor.edit_card.delete" diff --git a/src/translations/en.json b/src/translations/en.json index bac990dfb5..8417a30aef 100644 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -774,7 +774,8 @@ "hub": "Connected via", "firmware": "Firmware: {version}", "device_unavailable": "device unavailable", - "entity_unavailable": "entity unavailable" + "entity_unavailable": "entity unavailable", + "no_area": "No Area" } }, "users": { @@ -847,7 +848,8 @@ "toggle_editor": "Toggle Editor", "add": "Add Card", "edit": "Edit", - "delete": "Delete" + "delete": "Delete", + "move": "Move" }, "save_config": { "header": "Take control of your Lovelace UI", From 2482d78a061a84c2096e5b2d9172b8f7c10c2fff Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Tue, 5 Feb 2019 07:28:23 -0800 Subject: [PATCH 02/29] Optimize demo (#2681) --- config/{minimizer.js => webpack.js} | 17 +++++++++++++++++ demo/script/size_stats | 11 +++++++++++ demo/src/stubs/lovelace.ts | 23 +++++++++++++---------- demo/webpack.config.js | 13 +++++++------ webpack.config.js | 17 +++-------------- 5 files changed, 51 insertions(+), 30 deletions(-) rename config/{minimizer.js => webpack.js} (62%) create mode 100755 demo/script/size_stats diff --git a/config/minimizer.js b/config/webpack.js similarity index 62% rename from config/minimizer.js rename to config/webpack.js index ce16504520..a5a6384f00 100644 --- a/config/minimizer.js +++ b/config/webpack.js @@ -1,5 +1,22 @@ +const webpack = require("webpack"); +const path = require("path"); const BabelMinifyPlugin = require("babel-minify-webpack-plugin"); +module.exports.plugins = [ + // Ignore moment.js locales + new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/), + // Color.js is bloated, it contains all color definitions for all material color sets. + new webpack.NormalModuleReplacementPlugin( + /@polymer\/paper-styles\/color\.js$/, + path.resolve(__dirname, "../src/util/empty.js") + ), + // Ignore roboto pointing at CDN. We use local font-roboto-local. + new webpack.NormalModuleReplacementPlugin( + /@polymer\/font-roboto\/roboto\.js$/, + path.resolve(__dirname, "../src/util/empty.js") + ), +]; + module.exports.minimizer = [ // Took options from Polymer build tool // https://github.com/Polymer/tools/blob/master/packages/build/src/js-transform.ts diff --git a/demo/script/size_stats b/demo/script/size_stats new file mode 100755 index 0000000000..3afbe6c9a0 --- /dev/null +++ b/demo/script/size_stats @@ -0,0 +1,11 @@ +#!/bin/sh +# Analyze stats + +# Stop on errors +set -e + +cd "$(dirname "$0")/.." + +STATS=1 NODE_ENV=production ../node_modules/.bin/webpack --profile --json > compilation-stats.json +npx webpack-bundle-analyzer compilation-stats.json dist +rm compilation-stats.json diff --git a/demo/src/stubs/lovelace.ts b/demo/src/stubs/lovelace.ts index f3d4b300da..d1d6c2296b 100644 --- a/demo/src/stubs/lovelace.ts +++ b/demo/src/stubs/lovelace.ts @@ -3,7 +3,6 @@ import "../custom-cards/ha-demo-card"; // tslint:disable-next-line import { HADemoCard } from "../custom-cards/ha-demo-card"; import { MockHomeAssistant } from "../../../src/fake_data/provide_hass"; -import { HUIView } from "../../../src/panels/lovelace/hui-view"; import { selectedDemoConfig } from "../configs/demo-configs"; export const mockLovelace = (hass: MockHomeAssistant) => { @@ -16,13 +15,17 @@ export const mockLovelace = (hass: MockHomeAssistant) => { hass.mockWS("lovelace/config/save", () => Promise.resolve()); }; -// Patch HUI-VIEW to make the lovelace object available to the demo card -const oldCreateCard = HUIView.prototype.createCardElement; +customElements.whenDefined("hui-view").then(() => { + // tslint:disable-next-line + const HUIView = customElements.get("hui-view"); + // Patch HUI-VIEW to make the lovelace object available to the demo card + const oldCreateCard = HUIView.prototype.createCardElement; -HUIView.prototype.createCardElement = function(config) { - const el = oldCreateCard.call(this, config); - if (el.tagName === "HA-DEMO-CARD") { - (el as HADemoCard).lovelace = this.lovelace; - } - return el; -}; + HUIView.prototype.createCardElement = function(config) { + const el = oldCreateCard.call(this, config); + if (el.tagName === "HA-DEMO-CARD") { + (el as HADemoCard).lovelace = this.lovelace; + } + return el; + }; +}); diff --git a/demo/webpack.config.js b/demo/webpack.config.js index 851a983f30..e3f1b8023c 100644 --- a/demo/webpack.config.js +++ b/demo/webpack.config.js @@ -3,10 +3,12 @@ const webpack = require("webpack"); const CopyWebpackPlugin = require("copy-webpack-plugin"); const WorkboxPlugin = require("workbox-webpack-plugin"); const { babelLoaderConfig } = require("../config/babel.js"); -const { minimizer } = require("../config/babel.js"); +const webpackBase = require("../config/webpack.js"); const isProd = process.env.NODE_ENV === "production"; -const chunkFilename = isProd ? "chunk.[chunkhash].js" : "[name].chunk.js"; +const isStatsBuild = process.env.STATS === "1"; +const chunkFilename = + isProd && !isStatsBuild ? "chunk.[chunkhash].js" : "[name].chunk.js"; const buildPath = path.resolve(__dirname, "dist"); const publicPath = "/"; @@ -14,9 +16,7 @@ const latestBuild = false; module.exports = { mode: isProd ? "production" : "development", - // Disabled in prod while we make Home Assistant able to serve the right files. - // Was source-map - devtool: isProd ? "none" : "inline-source-map", + devtool: isProd ? "cheap-source-map" : "inline-source-map", entry: { main: "./src/entrypoint.ts", compatibility: "../src/entrypoints/compatibility.js", @@ -40,7 +40,7 @@ module.exports = { ], }, optimization: { - minimizer, + minimizer: webpackBase.minimizer, }, plugins: [ new webpack.DefinePlugin({ @@ -71,6 +71,7 @@ module.exports = { to: "static/images/leaflet/", }, ]), + ...webpackBase.plugins, isProd && new WorkboxPlugin.GenerateSW({ swDest: "service_worker_es5.js", diff --git a/webpack.config.js b/webpack.config.js index 46e71154de..fade775416 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -8,7 +8,7 @@ const HtmlWebpackPlugin = require("html-webpack-plugin"); const zopfli = require("@gfx/zopfli"); const translationMetadata = require("./build-translations/translationMetadata.json"); const { babelLoaderConfig } = require("./config/babel.js"); -const { minimizer } = require("./config/minimizer.js"); +const webpackBase = require("./config/webpack.js"); const version = fs.readFileSync("setup.py", "utf8").match(/\d{8}\.\d+/); if (!version) { @@ -77,7 +77,7 @@ function createConfig(isProdBuild, latestBuild) { ], }, optimization: { - minimizer, + minimizer: webpackBase.minimizer, }, plugins: [ new webpack.DefinePlugin({ @@ -123,18 +123,7 @@ function createConfig(isProdBuild, latestBuild) { !latestBuild && "public/__init__.py", ].filter(Boolean) ), - // Ignore moment.js locales - new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/), - // Color.js is bloated, it contains all color definitions for all material color sets. - new webpack.NormalModuleReplacementPlugin( - /@polymer\/paper-styles\/color\.js$/, - path.resolve(__dirname, "src/util/empty.js") - ), - // Ignore roboto pointing at CDN. We use local font-roboto-local. - new webpack.NormalModuleReplacementPlugin( - /@polymer\/font-roboto\/roboto\.js$/, - path.resolve(__dirname, "src/util/empty.js") - ), + ...webpackBase.plugins, isProdBuild && !isCI && !isStatsBuild && From bd0bc2047d92cf058eb761b291da1e4dfd796b0e Mon Sep 17 00:00:00 2001 From: yosilevy <37745463+yosilevy@users.noreply.github.com> Date: Tue, 5 Feb 2019 23:05:19 +0200 Subject: [PATCH 03/29] hui editor arrows RTL support (#2673) * hui editor arrows RTL support * Refactor * Refactor * Refactor --- src/panels/lovelace/hui-root.ts | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/panels/lovelace/hui-root.ts b/src/panels/lovelace/hui-root.ts index 043c61e784..2e0934a9e1 100644 --- a/src/panels/lovelace/hui-root.ts +++ b/src/panels/lovelace/hui-root.ts @@ -49,6 +49,7 @@ import { showEditLovelaceDialog } from "./editor/lovelace-editor/show-edit-lovel import { Lovelace } from "./types"; import { afterNextRender } from "../../common/util/render-status"; import { haStyle } from "../../resources/ha-style"; +import { computeRTL } from "../../common/util/compute_rtl"; // CSS and JS should only be imported once. Modules and HTML are safe. const CSS_CACHE = {}; @@ -252,7 +253,9 @@ class HUIRoot extends LitElement { @@ -277,7 +280,9 @@ class HUIRoot extends LitElement { + `; } @@ -78,6 +91,12 @@ class DemoGaugeEntity extends PolymerElement { }, }; } + + public ready() { + super.ready(); + const hass = provideHass(this.$.demos); + hass.addEntities(ENTITIES); + } } customElements.define("demo-hui-gauge-card", DemoGaugeEntity); From 5d900f9cedbb51e8bb1da9ea5abe126408b9506e Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Wed, 6 Feb 2019 10:57:53 -0800 Subject: [PATCH 05/29] Split unused entities by domain (#2671) --- src/panels/lovelace/hui-unused-entities.ts | 68 ++++++++++++++-------- 1 file changed, 44 insertions(+), 24 deletions(-) diff --git a/src/panels/lovelace/hui-unused-entities.ts b/src/panels/lovelace/hui-unused-entities.ts index 07a681d79f..10769275cc 100644 --- a/src/panels/lovelace/hui-unused-entities.ts +++ b/src/panels/lovelace/hui-unused-entities.ts @@ -12,11 +12,12 @@ import { createCardElement } from "./common/create-card-element"; import { HomeAssistant } from "../../types"; import { LovelaceCard } from "./types"; import { LovelaceConfig } from "../../data/lovelace"; +import computeDomain from "../../common/entity/compute_domain"; export class HuiUnusedEntities extends LitElement { private _hass?: HomeAssistant; private _config?: LovelaceConfig; - private _element?: LovelaceCard; + private _elements?: LovelaceCard[]; static get properties(): PropertyDeclarations { return { @@ -27,16 +28,18 @@ export class HuiUnusedEntities extends LitElement { set hass(hass: HomeAssistant) { this._hass = hass; - if (!this._element) { - this._createElement(); + if (!this._elements) { + this._createElements(); return; } - this._element.hass = this._hass; + for (const element of this._elements) { + element.hass = this._hass; + } } public setConfig(config: LovelaceConfig): void { this._config = config; - this._createElement(); + this._createElements(); } protected render(): TemplateResult | void { @@ -46,7 +49,7 @@ export class HuiUnusedEntities extends LitElement { return html` ${this.renderStyle()} -
${this._element}
+
${this._elements}
`; } @@ -54,30 +57,47 @@ export class HuiUnusedEntities extends LitElement { return html` `; } - private _createElement(): void { - if (this._hass) { - const entities = computeUnusedEntities(this._hass, this._config!).map( - (entity) => ({ - entity, - secondary_info: "entity-id", - }) - ); - this._element = createCardElement({ - type: "entities", - title: "Unused entities", - entities, - show_header_toggle: false, - }); - this._element!.hass = this._hass; + private _createElements(): void { + if (!this._hass) { + return; } + const domains: { [domain: string]: string[] } = {}; + computeUnusedEntities(this._hass, this._config!).forEach((entity) => { + const domain = computeDomain(entity); + + if (!(domain in domains)) { + domains[domain] = []; + } + domains[domain].push(entity); + }); + this._elements = Object.keys(domains) + .sort() + .map((domain) => { + const el = createCardElement({ + type: "entities", + title: this._hass!.localize(`domain.${domain}`) || domain, + entities: domains[domain].map((entity) => ({ + entity, + secondary_info: "entity-id", + })), + show_header_toggle: false, + }); + el.hass = this._hass; + return el; + }); } } From 7773589e2cd6eca906799a5009846be39ec79694 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Wed, 6 Feb 2019 10:59:47 -0800 Subject: [PATCH 06/29] Update Lit (#2692) --- package.json | 24 ++++++++++++------------ yarn.lock | 39 +++++++++++++++++---------------------- 2 files changed, 29 insertions(+), 34 deletions(-) diff --git a/package.json b/package.json index 17c029fdaf..3897186109 100644 --- a/package.json +++ b/package.json @@ -64,23 +64,21 @@ "@polymer/polymer": "^3.0.5", "@vaadin/vaadin-combo-box": "^4.2.0", "@vaadin/vaadin-date-picker": "^3.3.1", - "@webcomponents/shadycss": "^1.6.0", - "@webcomponents/webcomponentsjs": "^2.2.0", + "@webcomponents/shadycss": "^1.9.0", + "@webcomponents/webcomponentsjs": "^2.2.6", "chart.js": "~2.7.2", "chartjs-chart-timeline": "^0.2.1", "codemirror": "^5.43.0", "deep-clone-simple": "^1.1.1", "es6-object-assign": "^1.1.0", - "eslint-import-resolver-webpack": "^0.10.1", "fecha": "^3.0.0", - "gulp-hash-filename": "^2.0.1", "home-assistant-js-websocket": "^3.2.4", "intl-messageformat": "^2.2.0", "jquery": "^3.3.1", "js-yaml": "^3.12.0", "leaflet": "^1.3.4", - "lit-element": "2.0.0-rc.5", - "lit-html": "1.0.0-rc.2", + "lit-element": "^2.0.0", + "lit-html": "^1.0.0", "marked": "^0.6.0", "mdn-polyfills": "^5.12.0", "moment": "^2.22.2", @@ -113,20 +111,22 @@ "compression-webpack-plugin": "^2.0.0", "copy-webpack-plugin": "^4.5.2", "del": "^3.0.0", - "eslint": "^5.6.0", "eslint-config-airbnb-base": "^13.1.0", "eslint-config-prettier": "^4.0.0", + "eslint-import-resolver-webpack": "^0.10.1", "eslint-plugin-import": "^2.14.0", "eslint-plugin-prettier": "^3.0.0", "eslint-plugin-react": "^7.11.1", - "gulp": "^3.9.1", + "eslint": "^5.6.0", "gulp-foreach": "^0.1.0", + "gulp-hash-filename": "^2.0.1", "gulp-hash": "^4.2.2", "gulp-insert": "^0.5.0", "gulp-json-transform": "^0.4.5", "gulp-jsonminify": "^1.1.0", "gulp-merge-json": "^1.3.1", "gulp-rename": "^1.4.0", + "gulp": "^3.9.1", "html-loader": "^0.5.5", "html-webpack-plugin": "^3.2.0", "husky": "^1.1.0", @@ -141,23 +141,23 @@ "require-dir": "^1.0.0", "sinon": "^7.1.1", "ts-mocha": "^2.0.0", - "tslint": "^5.11.0", "tslint-config-prettier": "^1.15.0", "tslint-eslint-rules": "^5.4.0", "tslint-plugin-prettier": "^2.0.1", + "tslint": "^5.11.0", "typescript": "^3.1.4", "uglifyjs-webpack-plugin": "^2.1.1", "wct-browser-legacy": "^1.0.1", "web-component-tester": "^6.8.0", - "webpack": "^4.19.1", "webpack-cli": "^3.1.0", "webpack-dev-server": "^3.1.8", + "webpack": "^4.19.1", "workbox-webpack-plugin": "^3.5.0" }, "resolutions": { "@polymer/polymer": "3.1.0", - "@webcomponents/webcomponentsjs": "2.2.1", - "@webcomponents/shadycss": "^1.6.0", + "@webcomponents/webcomponentsjs": "^2.2.6", + "@webcomponents/shadycss": "^1.9.0", "@vaadin/vaadin-overlay": "3.2.2", "@vaadin/vaadin-lumo-styles": "1.3.0" }, diff --git a/yarn.lock b/yarn.lock index a0aebc0d47..200a944979 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2224,20 +2224,15 @@ "@webassemblyjs/wast-parser" "1.7.11" "@xtuc/long" "4.2.1" -"@webcomponents/shadycss@^1.5.2", "@webcomponents/shadycss@^1.6.0": - version "1.8.0" - resolved "https://registry.yarnpkg.com/@webcomponents/shadycss/-/shadycss-1.8.0.tgz#10340b87626f876841033b21b5221c3701252e10" - integrity sha512-bx0TzeZ11VqYDGLuXfznen8+4u0hADk2dD5RNMFxWL9MM4c5NXCDCgNXgssb4i2zQOos/GGe4tl5AuE0LzJkLA== +"@webcomponents/shadycss@^1.5.2", "@webcomponents/shadycss@^1.9.0": + version "1.9.0" + resolved "https://registry.yarnpkg.com/@webcomponents/shadycss/-/shadycss-1.9.0.tgz#8450465037370d4f5c32e801bb2554a7cf2f5037" + integrity sha512-g8Xa+6RSEME4g/wLJW4YII0eq15rvXp76RxPAuv7hx+Bdoi7GzZJ/EoZOUfyIbqAsQbII1TcWD4/+Xhs5NcM1w== -"@webcomponents/webcomponentsjs@2.2.1", "@webcomponents/webcomponentsjs@^1.0.7", "@webcomponents/webcomponentsjs@^2.0.0": - version "2.2.1" - resolved "https://registry.yarnpkg.com/@webcomponents/webcomponentsjs/-/webcomponentsjs-2.2.1.tgz#bf28cdad466aaf85d88ee56aebed32c83f4c1328" - integrity sha512-lZZ+Lkke6JhsJcQQqSVk1Pny6/8y4qhJ98LO7a/MwBSRO8WqHqK1X2vscfeL8vOnYGFnmBUyVG95lwYv/AXyLQ== - -"@webcomponents/webcomponentsjs@^2.2.0": - version "2.2.4" - resolved "https://registry.yarnpkg.com/@webcomponents/webcomponentsjs/-/webcomponentsjs-2.2.4.tgz#b520d586e46517914d9708c2220b263116d5f86a" - integrity sha512-YkLxK9Mbw6QK5bNZ67Rarb/yj0gN+Ziy5+2sLjM0lDb3XafM36gXKZXaIBz4zvLA/cYYTdg+l1LlqXeHEcmeiA== +"@webcomponents/webcomponentsjs@^1.0.7", "@webcomponents/webcomponentsjs@^2.0.0", "@webcomponents/webcomponentsjs@^2.2.6": + version "2.2.6" + resolved "https://registry.yarnpkg.com/@webcomponents/webcomponentsjs/-/webcomponentsjs-2.2.6.tgz#b6aa1f837f05ec2a6996eb09185d5ade042ba4a9" + integrity sha512-TbuyV7hHIq4jMlvQ8pF3C6QNEqFeBX4be9AND5DO8UCRRYcZBpjl1yH9IChbebeWr1QNcK5WlZC3voAlQCdisQ== "@xtuc/ieee754@^1.2.0": version "1.2.0" @@ -8660,17 +8655,17 @@ listr@^0.14.2: p-map "^2.0.0" rxjs "^6.3.3" -lit-element@2.0.0-rc.5: - version "2.0.0-rc.5" - resolved "https://registry.yarnpkg.com/lit-element/-/lit-element-2.0.0-rc.5.tgz#3e4323c9ace59d49c3a0950551a3f923e0ccf86b" - integrity sha512-cMmWNWSFyYfXAd09bnqFhqDr5kuR/5guImD5ZRTk223EiBJaoo7naZnQngSYAMjgDn1CSbTE1LRtzviMS+g0RA== +lit-element@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/lit-element/-/lit-element-2.0.1.tgz#9ec5871d3b64487f432c7c071df80ef031d7091b" + integrity sha512-2bu3B2ZYUZgntvOgu3i5mRK8geo45CLUwxwJEYK54hyednoJasjiTZPB13NBg1D+hNM2JfmWTWJnh1QEUQv7zw== dependencies: - lit-html "^1.0.0-rc.2" + lit-html "^1.0.0" -lit-html@1.0.0-rc.2, lit-html@^1.0.0-rc.2: - version "1.0.0-rc.2" - resolved "https://registry.yarnpkg.com/lit-html/-/lit-html-1.0.0-rc.2.tgz#b9c904520fe005d349aa737a86d83645d97d5a89" - integrity sha512-4bq34lhVmwWly1zBXicOBJLOwaWfjOVbchEEmFnZLuztxjh5wRd2WqV0URX8Q47MQ7PaIjn/eXyTRKsYhSAeRw== +lit-html@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/lit-html/-/lit-html-1.0.0.tgz#3dc3781a8ca68a9b5c2ff2a61e263662b9b2267b" + integrity sha512-oeWlpLmBW3gFl7979Wol2LKITpmKTUFNn7PnFbh6YNynF61W74l6x5WhwItAwPRSATpexaX1egNnRzlN4GOtfQ== load-json-file@^1.0.0: version "1.1.0" From ce354162844d4f3297abd0c4e77e1a04cf513d86 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Wed, 6 Feb 2019 11:00:17 -0800 Subject: [PATCH 07/29] Migrate more-info-content to UpdatingElement (#2693) --- ...e-info-content.js => more-info-content.ts} | 53 +++++++++++-------- 1 file changed, 32 insertions(+), 21 deletions(-) rename src/dialogs/more-info/controls/{more-info-content.js => more-info-content.ts} (57%) diff --git a/src/dialogs/more-info/controls/more-info-content.js b/src/dialogs/more-info/controls/more-info-content.ts similarity index 57% rename from src/dialogs/more-info/controls/more-info-content.js rename to src/dialogs/more-info/controls/more-info-content.ts index 2d4f20a7c1..e60c03b6e0 100644 --- a/src/dialogs/more-info/controls/more-info-content.js +++ b/src/dialogs/more-info/controls/more-info-content.ts @@ -1,4 +1,9 @@ -import { PolymerElement } from "@polymer/polymer/polymer-element"; +import { + PropertyDeclarations, + PropertyValues, + UpdatingElement, +} from "lit-element"; +import { HassEntity } from "home-assistant-js-websocket"; import "./more-info-alarm_control_panel"; import "./more-info-automation"; @@ -23,26 +28,30 @@ import "./more-info-weather"; import stateMoreInfoType from "../../../common/entity/state_more_info_type"; import dynamicContentUpdater from "../../../common/dom/dynamic_content_updater"; +import { HomeAssistant } from "../../../types"; -class MoreInfoContent extends PolymerElement { - static get properties() { +class MoreInfoContent extends UpdatingElement { + public hass?: HomeAssistant; + public stateObj?: HassEntity; + private _detachedChild?: ChildNode; + + static get properties(): PropertyDeclarations { return { - hass: Object, - stateObj: Object, + hass: {}, + stateObj: {}, }; } - static get observers() { - return ["stateObjChanged(stateObj, hass)"]; - } - - constructor() { - super(); + protected firstUpdated(): void { this.style.display = "block"; } - stateObjChanged(stateObj, hass) { - let moreInfoType; + // This is not a lit element, but an updating element, so we implement update + protected update(changedProps: PropertyValues): void { + super.update(changedProps); + const stateObj = this.stateObj; + const hass = this.hass; + if (!stateObj || !hass) { if (this.lastChild) { this._detachedChild = this.lastChild; @@ -51,18 +60,20 @@ class MoreInfoContent extends PolymerElement { } return; } + if (this._detachedChild) { this.appendChild(this._detachedChild); - this._detachedChild = null; - } - if (stateObj.attributes && "custom_ui_more_info" in stateObj.attributes) { - moreInfoType = stateObj.attributes.custom_ui_more_info; - } else { - moreInfoType = "more-info-" + stateMoreInfoType(stateObj); + this._detachedChild = undefined; } + + const moreInfoType = + stateObj.attributes && "custom_ui_more_info" in stateObj.attributes + ? stateObj.attributes.custom_ui_more_info + : "more-info-" + stateMoreInfoType(stateObj); + dynamicContentUpdater(this, moreInfoType.toUpperCase(), { - hass: hass, - stateObj: stateObj, + hass, + stateObj, }); } } From f00de454d1f3b17093b7668025f20267d32c9ffb Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Wed, 6 Feb 2019 11:01:15 -0800 Subject: [PATCH 08/29] Convert automation editor to Lit (#2687) * Convert automation editor to Lit * Apply suggestions from code review Co-Authored-By: balloob --- src/data/automation.ts | 17 + .../config/automation/ha-automation-editor.js | 298 ------------------ .../config/automation/ha-automation-editor.ts | 277 ++++++++++++++++ 3 files changed, 294 insertions(+), 298 deletions(-) create mode 100644 src/data/automation.ts delete mode 100644 src/panels/config/automation/ha-automation-editor.js create mode 100644 src/panels/config/automation/ha-automation-editor.ts diff --git a/src/data/automation.ts b/src/data/automation.ts new file mode 100644 index 0000000000..02032b06c4 --- /dev/null +++ b/src/data/automation.ts @@ -0,0 +1,17 @@ +import { + HassEntityBase, + HassEntityAttributeBase, +} from "home-assistant-js-websocket"; + +export interface AutomationEntity extends HassEntityBase { + attributes: HassEntityAttributeBase & { + id?: string; + }; +} + +export interface AutomationConfig { + alias: string; + trigger: any[]; + condition?: any[]; + action: any[]; +} diff --git a/src/panels/config/automation/ha-automation-editor.js b/src/panels/config/automation/ha-automation-editor.js deleted file mode 100644 index 45e010facb..0000000000 --- a/src/panels/config/automation/ha-automation-editor.js +++ /dev/null @@ -1,298 +0,0 @@ -import "@polymer/app-layout/app-header/app-header"; -import "@polymer/app-layout/app-toolbar/app-toolbar"; -import "@polymer/paper-icon-button/paper-icon-button"; -import "@polymer/paper-fab/paper-fab"; - -import { html } from "@polymer/polymer/lib/utils/html-tag"; -import { PolymerElement } from "@polymer/polymer/polymer-element"; -import { h, render } from "preact"; - -import "../../../layouts/ha-app-layout"; - -import Automation from "../js/automation"; -import unmountPreact from "../../../common/preact/unmount"; -import computeStateName from "../../../common/entity/compute_state_name"; -import NavigateMixin from "../../../mixins/navigate-mixin"; -import LocalizeMixin from "../../../mixins/localize-mixin"; - -function AutomationEditor(mountEl, props, mergeEl) { - return render(h(Automation, props), mountEl, mergeEl); -} - -/* - * @appliesMixin LocalizeMixin - * @appliesMixin NavigateMixin - */ -class HaAutomationEditor extends LocalizeMixin(NavigateMixin(PolymerElement)) { - static get template() { - return html` - - - - - - -
[[computeName(automation, localize)]]
-
-
- -
- -
-
- -
- `; - } - - static get properties() { - return { - hass: { - type: Object, - observer: "_updateComponent", - }, - - narrow: { - type: Boolean, - }, - - showMenu: { - type: Boolean, - value: false, - }, - - errors: { - type: Object, - value: null, - }, - - dirty: { - type: Boolean, - value: false, - }, - - config: { - type: Object, - value: null, - }, - - automation: { - type: Object, - observer: "automationChanged", - }, - - creatingNew: { - type: Boolean, - observer: "creatingNewChanged", - }, - - isWide: { - type: Boolean, - observer: "_updateComponent", - }, - - _rendered: { - type: Object, - value: null, - }, - - _renderScheduled: { - type: Boolean, - value: false, - }, - }; - } - - ready() { - this.configChanged = this.configChanged.bind(this); - super.ready(); // This call will initialize preact. - } - - disconnectedCallback() { - super.disconnectedCallback(); - if (this._rendered) { - unmountPreact(this._rendered); - this._rendered = null; - } - } - - configChanged(config) { - // onChange gets called a lot during initial rendering causing recursing calls. - if (this._rendered === null) return; - this.config = config; - this.errors = null; - this.dirty = true; - this._updateComponent(); - } - - automationChanged(newVal, oldVal) { - if (!newVal) return; - if (!this.hass) { - setTimeout(() => this.automationChanged(newVal, oldVal), 0); - return; - } - if (oldVal && oldVal.attributes.id === newVal.attributes.id) { - return; - } - this.hass - .callApi("get", "config/automation/config/" + newVal.attributes.id) - .then( - function(config) { - // Normalize data: ensure trigger, action and condition are lists - // Happens when people copy paste their automations into the config - ["trigger", "condition", "action"].forEach(function(key) { - var value = config[key]; - if (value && !Array.isArray(value)) { - config[key] = [value]; - } - }); - this.dirty = false; - this.config = config; - this._updateComponent(); - }.bind(this) - ); - } - - creatingNewChanged(newVal) { - if (!newVal) { - return; - } - this.dirty = false; - this.config = { - alias: this.localize("ui.panel.config.automation.editor.default_name"), - trigger: [{ platform: "state" }], - condition: [], - action: [{ service: "" }], - }; - this._updateComponent(); - } - - backTapped() { - if ( - this.dirty && - // eslint-disable-next-line - !confirm( - this.localize("ui.panel.config.automation.editor.unsaved_confirm") - ) - ) { - return; - } - history.back(); - } - - async _updateComponent() { - if (this._renderScheduled || !this.hass || !this.config) return; - this._renderScheduled = true; - - await 0; - - if (!this._renderScheduled) return; - - this._renderScheduled = false; - - this._rendered = AutomationEditor( - this.$.root, - { - automation: this.config, - onChange: this.configChanged, - isWide: this.isWide, - hass: this.hass, - localize: this.localize, - }, - this._rendered - ); - } - - saveAutomation() { - var id = this.creatingNew ? "" + Date.now() : this.automation.attributes.id; - this.hass - .callApi("post", "config/automation/config/" + id, this.config) - .then( - function() { - this.dirty = false; - - if (this.creatingNew) { - this.navigate(`/config/automation/edit/${id}`, true); - } - }.bind(this), - function(errors) { - this.errors = errors.body.message; - throw errors; - }.bind(this) - ); - } - - computeName(automation, localize) { - return automation - ? computeStateName(automation) - : localize("ui.panel.config.automation.editor.default_name"); - } -} - -customElements.define("ha-automation-editor", HaAutomationEditor); diff --git a/src/panels/config/automation/ha-automation-editor.ts b/src/panels/config/automation/ha-automation-editor.ts new file mode 100644 index 0000000000..4d00ececa8 --- /dev/null +++ b/src/panels/config/automation/ha-automation-editor.ts @@ -0,0 +1,277 @@ +import { + LitElement, + TemplateResult, + html, + CSSResult, + css, + PropertyDeclarations, + PropertyValues, +} from "lit-element"; +import "@polymer/app-layout/app-header/app-header"; +import "@polymer/app-layout/app-toolbar/app-toolbar"; +import "@polymer/paper-icon-button/paper-icon-button"; +import "@polymer/paper-fab/paper-fab"; + +import { h, render } from "preact"; + +import "../../../layouts/ha-app-layout"; + +import Automation from "../js/automation"; +import unmountPreact from "../../../common/preact/unmount"; +import computeStateName from "../../../common/entity/compute_state_name"; + +import { haStyle } from "../../../resources/ha-style"; +import { HomeAssistant } from "../../../types"; +import { AutomationEntity, AutomationConfig } from "../../../data/automation"; +import { navigate } from "../../../common/navigate"; + +function AutomationEditor(mountEl, props, mergeEl) { + return render(h(Automation, props), mountEl, mergeEl); +} + +class HaAutomationEditor extends LitElement { + public hass?: HomeAssistant; + public automation?: AutomationEntity; + public isWide?: boolean; + public creatingNew?: boolean; + private _config?: AutomationConfig; + private _dirty?: boolean; + private _rendered?: unknown; + private _errors?: string; + + static get properties(): PropertyDeclarations { + return { + hass: {}, + automation: {}, + creatingNew: {}, + isWide: {}, + _errors: {}, + _dirty: {}, + _config: {}, + }; + } + + constructor() { + super(); + this._configChanged = this._configChanged.bind(this); + } + + public disconnectedCallback(): void { + super.disconnectedCallback(); + if (this._rendered) { + unmountPreact(this._rendered); + this._rendered = undefined; + } + } + + protected render(): TemplateResult | void { + if (!this.hass) { + return; + } + return html` + + + + +
+ ${this.automation + ? computeStateName(this.automation) + : this.hass.localize( + "ui.panel.config.automation.editor.default_name" + )} +
+
+
+ +
+ ${this._errors + ? html` +
${this._errors}
+ ` + : ""} +
+
+ +
+ `; + } + + protected updated(changedProps: PropertyValues): void { + super.updated(changedProps); + + const oldAutomation = changedProps.get("automation") as AutomationEntity; + if ( + changedProps.has("automation") && + this.automation && + this.hass && + // Only refresh config if we picked a new automation. If same ID, don't fetch it. + (!oldAutomation || + oldAutomation.attributes.id !== this.automation.attributes.id) + ) { + this.hass + .callApi( + "GET", + `config/automation/config/${this.automation.attributes.id}` + ) + .then((config) => { + // Normalize data: ensure trigger, action and condition are lists + // Happens when people copy paste their automations into the config + for (const key of ["trigger", "condition", "action"]) { + const value = config[key]; + if (value && !Array.isArray(value)) { + config[key] = [value]; + } + } + this._dirty = false; + this._config = config; + }); + } + + if (changedProps.has("creatingNew") && this.creatingNew && this.hass) { + this._dirty = false; + this._config = { + alias: this.hass.localize( + "ui.panel.config.automation.editor.default_name" + ), + trigger: [{ platform: "state" }], + condition: [], + action: [{ service: "" }], + }; + } + + if (changedProps.has("_config") && this.hass) { + this._rendered = AutomationEditor( + this.shadowRoot!.querySelector("#root"), + { + automation: this._config, + onChange: this._configChanged, + isWide: this.isWide, + hass: this.hass, + localize: this.hass.localize, + }, + this._rendered + ); + } + } + + private _configChanged(config: AutomationConfig): void { + // onChange gets called a lot during initial rendering causing recursing calls. + if (!this._rendered) { + return; + } + this._config = config; + this._errors = undefined; + this._dirty = true; + // this._updateComponent(); + } + + private _backTapped(): void { + if ( + this._dirty && + !confirm( + this.hass!.localize("ui.panel.config.automation.editor.unsaved_confirm") + ) + ) { + return; + } + history.back(); + } + + private _saveAutomation(): void { + const id = this.creatingNew + ? "" + Date.now() + : this.automation!.attributes.id; + this.hass!.callApi( + "POST", + "config/automation/config/" + id, + this._config + ).then( + () => { + this._dirty = false; + + if (this.creatingNew) { + navigate(this, `/config/automation/edit/${id}`, true); + } + }, + (errors) => { + this._errors = errors.body.message; + throw errors; + } + ); + } + + static get styles(): CSSResult[] { + return [ + haStyle, + css` + .errors { + padding: 20px; + font-weight: bold; + color: var(--google-red-500); + } + .content { + padding-bottom: 20px; + } + paper-card { + display: block; + } + .triggers, + .script { + margin-top: -16px; + } + .triggers paper-card, + .script paper-card { + margin-top: 16px; + } + .add-card paper-button { + display: block; + text-align: center; + } + .card-menu { + position: absolute; + top: 0; + right: 0; + z-index: 1; + color: var(--primary-text-color); + } + .card-menu paper-item { + cursor: pointer; + } + span[slot="introduction"] a { + color: var(--primary-color); + } + paper-fab { + position: fixed; + bottom: 16px; + right: 16px; + z-index: 1; + margin-bottom: -80px; + transition: margin-bottom 0.3s; + } + + paper-fab[is-wide] { + bottom: 24px; + right: 24px; + } + + paper-fab[dirty] { + margin-bottom: 0; + } + `, + ]; + } +} + +customElements.define("ha-automation-editor", HaAutomationEditor); From 504e4987b7a4fe96d8459814ed8dc42f6a217a67 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Wed, 6 Feb 2019 11:13:00 -0800 Subject: [PATCH 09/29] Fix event action in automation editor (#2686) * Fix event action in automation editor * Fix webpack resolve * Update ha-automation-editor.js --- config/babel.js | 9 ++- config/webpack.js | 60 ++++++++++++------- demo/webpack.config.js | 16 +---- gallery/webpack.config.js | 10 +--- src/data/script.ts | 5 ++ src/panels/config/js/preact-types.ts | 12 ++++ .../config/js/script/{event.js => event.tsx} | 47 ++++++++++----- tsconfig.json | 2 + webpack.config.js | 16 +---- 9 files changed, 101 insertions(+), 76 deletions(-) create mode 100644 src/data/script.ts create mode 100644 src/panels/config/js/preact-types.ts rename src/panels/config/js/script/{event.js => event.tsx} (53%) diff --git a/config/babel.js b/config/babel.js index 1c073245dc..3a9fbe452c 100644 --- a/config/babel.js +++ b/config/babel.js @@ -3,7 +3,7 @@ module.exports.babelLoaderConfig = ({ latestBuild }) => { throw Error("latestBuild not defined for babel loader config"); } return { - test: /\.m?js$|\.ts$/, + test: /\.m?js$|\.tsx?$/, use: { loader: "babel-loader", options: { @@ -12,7 +12,12 @@ module.exports.babelLoaderConfig = ({ latestBuild }) => { require("@babel/preset-env").default, { modules: false }, ], - require("@babel/preset-typescript").default, + [ + require("@babel/preset-typescript").default, + { + jsxPragma: "h", + }, + ], ].filter(Boolean), plugins: [ // Part of ES2018. Converts {...a, b: 2} to Object.assign({}, a, {b: 2}) diff --git a/config/webpack.js b/config/webpack.js index a5a6384f00..118fb0e23f 100644 --- a/config/webpack.js +++ b/config/webpack.js @@ -2,6 +2,18 @@ const webpack = require("webpack"); const path = require("path"); const BabelMinifyPlugin = require("babel-minify-webpack-plugin"); +module.exports.resolve = { + extensions: [".ts", ".js", ".json", ".tsx"], + alias: { + react: "preact-compat", + "react-dom": "preact-compat", + // Not necessary unless you consume a module using `createClass` + "create-react-class": "preact-compat/lib/create-react-class", + // Not necessary unless you consume a module requiring `react-dom-factories` + "react-dom-factories": "preact-compat/lib/react-dom-factories", + }, +}; + module.exports.plugins = [ // Ignore moment.js locales new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/), @@ -17,30 +29,32 @@ module.exports.plugins = [ ), ]; -module.exports.minimizer = [ - // Took options from Polymer build tool - // https://github.com/Polymer/tools/blob/master/packages/build/src/js-transform.ts - new BabelMinifyPlugin( - { - // Disable the minify-constant-folding plugin because it has a bug relating - // to invalid substitution of constant values into export specifiers: - // https://github.com/babel/minify/issues/820 - evaluate: false, +module.exports.optimization = { + minimizer: [ + // Took options from Polymer build tool + // https://github.com/Polymer/tools/blob/master/packages/build/src/js-transform.ts + new BabelMinifyPlugin( + { + // Disable the minify-constant-folding plugin because it has a bug relating + // to invalid substitution of constant values into export specifiers: + // https://github.com/babel/minify/issues/820 + evaluate: false, - // TODO(aomarks) Find out why we disabled this plugin. - simplifyComparisons: false, + // TODO(aomarks) Find out why we disabled this plugin. + simplifyComparisons: false, - // Prevent removal of things that babel thinks are unreachable, but sometimes - // gets wrong: https://github.com/Polymer/tools/issues/724 - deadcode: false, + // Prevent removal of things that babel thinks are unreachable, but sometimes + // gets wrong: https://github.com/Polymer/tools/issues/724 + deadcode: false, - // Disable the simplify plugin because it can eat some statements preceeding - // loops. https://github.com/babel/minify/issues/824 - simplify: false, + // Disable the simplify plugin because it can eat some statements preceeding + // loops. https://github.com/babel/minify/issues/824 + simplify: false, - // This is breaking ES6 output. https://github.com/Polymer/tools/issues/261 - mangle: false, - }, - {} - ), -]; + // This is breaking ES6 output. https://github.com/Polymer/tools/issues/261 + mangle: false, + }, + {} + ), + ], +}; diff --git a/demo/webpack.config.js b/demo/webpack.config.js index e3f1b8023c..3ba0f389df 100644 --- a/demo/webpack.config.js +++ b/demo/webpack.config.js @@ -39,9 +39,7 @@ module.exports = { }, ], }, - optimization: { - minimizer: webpackBase.minimizer, - }, + optimization: webpackBase.optimization, plugins: [ new webpack.DefinePlugin({ __DEV__: false, @@ -78,17 +76,7 @@ module.exports = { importWorkboxFrom: "local", }), ].filter(Boolean), - resolve: { - extensions: [".ts", ".js", ".json"], - alias: { - react: "preact-compat", - "react-dom": "preact-compat", - // Not necessary unless you consume a module using `createClass` - "create-react-class": "preact-compat/lib/create-react-class", - // Not necessary unless you consume a module requiring `react-dom-factories` - "react-dom-factories": "preact-compat/lib/react-dom-factories", - }, - }, + resolve: webpackBase.resolve, output: { filename: "[name].js", chunkFilename: chunkFilename, diff --git a/gallery/webpack.config.js b/gallery/webpack.config.js index 032efa198d..763bed55c7 100644 --- a/gallery/webpack.config.js +++ b/gallery/webpack.config.js @@ -1,7 +1,7 @@ const path = require("path"); const CopyWebpackPlugin = require("copy-webpack-plugin"); const { babelLoaderConfig } = require("../config/babel.js"); -const { minimizer } = require("../config/babel.js"); +const webpackBase = require("../config/babel.js"); const isProd = process.env.NODE_ENV === "production"; const chunkFilename = isProd ? "chunk.[chunkhash].js" : "[name].chunk.js"; @@ -32,9 +32,7 @@ module.exports = { }, ], }, - optimization: { - minimizer, - }, + optimization: webpackBase.optimization, plugins: [ new CopyWebpackPlugin([ "public", @@ -63,9 +61,7 @@ module.exports = { }, }), ].filter(Boolean), - resolve: { - extensions: [".ts", ".js", ".json"], - }, + resolve: webpackBase.resolve, output: { filename: "[name].js", chunkFilename: chunkFilename, diff --git a/src/data/script.ts b/src/data/script.ts new file mode 100644 index 0000000000..0c50a3f6e6 --- /dev/null +++ b/src/data/script.ts @@ -0,0 +1,5 @@ +export interface EventAction { + event: string; + event_data?: { [key: string]: any }; + event_data_template?: { [key: string]: any }; +} diff --git a/src/panels/config/js/preact-types.ts b/src/panels/config/js/preact-types.ts new file mode 100644 index 0000000000..95d790ede4 --- /dev/null +++ b/src/panels/config/js/preact-types.ts @@ -0,0 +1,12 @@ +import { PaperInputElement } from "@polymer/paper-input/paper-input"; + +// Force file to be a module to augment global scope. +export {}; + +declare global { + namespace JSX { + interface IntrinsicElements { + "paper-input": Partial; + } + } +} diff --git a/src/panels/config/js/script/event.js b/src/panels/config/js/script/event.tsx similarity index 53% rename from src/panels/config/js/script/event.js rename to src/panels/config/js/script/event.tsx index f11eb12170..43d7c30f79 100644 --- a/src/panels/config/js/script/event.js +++ b/src/panels/config/js/script/event.tsx @@ -3,8 +3,26 @@ import "@polymer/paper-input/paper-input"; import JSONTextArea from "../json_textarea"; import { onChangeEvent } from "../../../../common/preact/event"; +import { LocalizeFunc } from "../../../../common/translations/localize"; +import { EventAction } from "../../../../data/script"; + +interface Props { + index: number; + action: EventAction; + localize: LocalizeFunc; + onChange: (index: number, action: EventAction) => void; +} + +export default class EventActionForm extends Component { + private onChange: (event: Event) => void; + + static get defaultConfig(): EventAction { + return { + event: "", + event_data: {}, + }; + } -export default class EventAction extends Component { constructor() { super(); @@ -12,16 +30,11 @@ export default class EventAction extends Component { this.serviceDataChanged = this.serviceDataChanged.bind(this); } - serviceDataChanged(data) { - this.props.onChange( - this.props.index, - Object.assign({}, this.props.action, { data }) - ); - } - - render({ action, localize }) { - /* eslint-disable camelcase */ - const { event, event_data } = action; + public render() { + const { + action: { event, event_data }, + localize, + } = this.props; return (
); } -} -EventAction.defaultConfig = { - event: "", - event_data: {}, -}; + private serviceDataChanged(eventData) { + this.props.onChange(this.props.index, { + ...this.props.action, + event_data: eventData, + }); + } +} diff --git a/tsconfig.json b/tsconfig.json index 387ca8661e..b9c04a1dbe 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,5 +1,7 @@ { "compilerOptions": { + "jsx": "react", + "jsxFactory": "h", "target": "es2017", "module": "esnext", "moduleResolution": "node", diff --git a/webpack.config.js b/webpack.config.js index fade775416..ec6d52dc1a 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -76,9 +76,7 @@ function createConfig(isProdBuild, latestBuild) { }, ], }, - optimization: { - minimizer: webpackBase.minimizer, - }, + optimization: webpackBase.optimization, plugins: [ new webpack.DefinePlugin({ __DEV__: JSON.stringify(!isProdBuild), @@ -196,17 +194,7 @@ function createConfig(isProdBuild, latestBuild) { path: path.resolve(__dirname, buildPath), publicPath, }, - resolve: { - extensions: [".ts", ".js", ".json"], - alias: { - react: "preact-compat", - "react-dom": "preact-compat", - // Not necessary unless you consume a module using `createClass` - "create-react-class": "preact-compat/lib/create-react-class", - // Not necessary unless you consume a module requiring `react-dom-factories` - "react-dom-factories": "preact-compat/lib/react-dom-factories", - }, - }, + resolve: webpackBase.resolve, }; } From d3bdbce0d0b3afea6092907b3e13dcfd8c49f217 Mon Sep 17 00:00:00 2001 From: Robert Schindler Date: Thu, 7 Feb 2019 01:38:31 +0100 Subject: [PATCH 10/29] Added strings for command line auth provider (#2561) * Added strings for command line auth provider Regards home-assistant/home-assistant#19985 * Reuse existing translation keys for new command_line auth provider --- src/translations/en.json | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/src/translations/en.json b/src/translations/en.json index 8417a30aef..67e74eb591 100644 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -958,6 +958,29 @@ "working": "Please wait", "unknown_error": "Something went wrong", "providers": { + "command_line": { + "step": { + "init": { + "data": { + "username": "[%key:ui::panel::page-authorize::form::providers::homeassistant::step::init::data::username%]", + "password": "[%key:ui::panel::page-authorize::form::providers::homeassistant::step::init::data::password%]" + } + }, + "mfa": { + "data": { + "code": "[%key:ui::panel::page-authorize::form::providers::homeassistant::step::mfa::data::code%]" + }, + "description": "[%key:ui::panel::page-authorize::form::providers::homeassistant::step::mfa::description%]" + } + }, + "error": { + "invalid_auth": "[%key:ui::panel::page-authorize::form::providers::homeassistant::error::invalid_auth%]", + "invalid_code": "[%key:ui::panel::page-authorize::form::providers::homeassistant::error::invalid_code%]" + }, + "abort": { + "login_expired": "[%key:ui::panel::page-authorize::form::providers::homeassistant::abort::login_expired%]" + } + }, "homeassistant": { "step": { "init": { From 7a16961387e1be5e0b2826c722d732b09548dbd0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Lov=C3=A9n?= Date: Thu, 7 Feb 2019 22:30:02 +0100 Subject: [PATCH 11/29] Add background and border-radius to themeable options for ha-card (#2700) * Add background and border-radius to themeable options for ha-card * Change variable names --- src/components/ha-card.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/ha-card.js b/src/components/ha-card.js index f497d69d02..bed6d2446f 100644 --- a/src/components/ha-card.js +++ b/src/components/ha-card.js @@ -9,9 +9,9 @@ class HaCard extends PolymerElement { :host { @apply --paper-material-elevation-1; display: block; - border-radius: 2px; + border-radius: var(--ha-card-border-radius, 2px); transition: all 0.3s ease-out; - background-color: var(--paper-card-background-color, white); + background: var(--ha-card-background, var(--paper-card-background-color, white)); color: var(--primary-text-color); } .header { From 17d0ae003acf98dc716d5a202b4444135bcad498 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Thu, 7 Feb 2019 17:16:39 -0800 Subject: [PATCH 12/29] Lint --- src/components/ha-card.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/components/ha-card.js b/src/components/ha-card.js index bed6d2446f..44e58f43e9 100644 --- a/src/components/ha-card.js +++ b/src/components/ha-card.js @@ -11,7 +11,10 @@ class HaCard extends PolymerElement { display: block; border-radius: var(--ha-card-border-radius, 2px); transition: all 0.3s ease-out; - background: var(--ha-card-background, var(--paper-card-background-color, white)); + background: var( + --ha-card-background, + var(--paper-card-background-color, white) + ); color: var(--primary-text-color); } .header { From 102cb06d288c761c4c106d474bd2860ea1d7f2da Mon Sep 17 00:00:00 2001 From: yosilevy <37745463+yosilevy@users.noreply.github.com> Date: Fri, 8 Feb 2019 05:32:32 +0200 Subject: [PATCH 13/29] RTL support for arrows in scrolable tabs (#2696) * RTL support for arrows in scrolable tabs * Refactor --- src/panels/lovelace/hui-root.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/panels/lovelace/hui-root.ts b/src/panels/lovelace/hui-root.ts index 2e0934a9e1..3c07001b5f 100644 --- a/src/panels/lovelace/hui-root.ts +++ b/src/panels/lovelace/hui-root.ts @@ -49,7 +49,7 @@ import { showEditLovelaceDialog } from "./editor/lovelace-editor/show-edit-lovel import { Lovelace } from "./types"; import { afterNextRender } from "../../common/util/render-status"; import { haStyle } from "../../resources/ha-style"; -import { computeRTL } from "../../common/util/compute_rtl"; +import { computeRTL, computeRTLDirection } from "../../common/util/compute_rtl"; // CSS and JS should only be imported once. Modules and HTML are safe. const CSS_CACHE = {}; @@ -244,6 +244,7 @@ class HUIRoot extends LitElement { scrollable .selected="${this._curView}" @iron-activate="${this._handleViewSelected}" + dir="${computeRTLDirection(this.hass!)}" > ${this.lovelace!.config.views.map( (view) => html` From 8938ad8f8d783c586adf43faff16e4180a51486b Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Fri, 8 Feb 2019 04:33:53 +0100 Subject: [PATCH 14/29] Add ctrl/cmd +s support back to editor (#2694) * Add ctrl/cmd +s support back to editor * Update hui-yaml-editor.ts --- src/panels/lovelace/components/hui-yaml-editor.ts | 4 ++++ src/panels/lovelace/editor/card-editor/hui-edit-card.ts | 1 + src/panels/lovelace/hui-editor.ts | 5 ++++- 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/panels/lovelace/components/hui-yaml-editor.ts b/src/panels/lovelace/components/hui-yaml-editor.ts index f83d392bee..ae2a9b65ad 100644 --- a/src/panels/lovelace/components/hui-yaml-editor.ts +++ b/src/panels/lovelace/components/hui-yaml-editor.ts @@ -9,6 +9,7 @@ declare global { "yaml-changed": { value: string; }; + "yaml-save": undefined; } } @@ -18,6 +19,9 @@ export class HuiYamlEditor extends HTMLElement { public constructor() { super(); + CodeMirror.commands.save = (cm: CodeMirror) => { + fireEvent(cm.getWrapperElement(), "yaml-save"); + }; this._value = ""; const shadowRoot = this.attachShadow({ mode: "open" }); shadowRoot.innerHTML = ` diff --git a/src/panels/lovelace/editor/card-editor/hui-edit-card.ts b/src/panels/lovelace/editor/card-editor/hui-edit-card.ts index 3898e7b9ac..0022c045b2 100644 --- a/src/panels/lovelace/editor/card-editor/hui-edit-card.ts +++ b/src/panels/lovelace/editor/card-editor/hui-edit-card.ts @@ -121,6 +121,7 @@ export class HuiEditCard extends LitElement { `}
diff --git a/src/panels/lovelace/hui-editor.ts b/src/panels/lovelace/hui-editor.ts index fcdb102773..87bdd6daed 100644 --- a/src/panels/lovelace/hui-editor.ts +++ b/src/panels/lovelace/hui-editor.ts @@ -61,7 +61,10 @@ class LovelaceFullConfigEditor extends LitElement {
- +
From 46e11399467f4c3d6cfb88b6db78432f1d1f19ab Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Sat, 9 Feb 2019 10:41:45 -0800 Subject: [PATCH 15/29] Add MVP person editor (#2703) * Add MVP person editor * Better highlight the config.yaml people * Add note --- src/data/person.ts | 46 ++++ .../show-dialog-area-registry-detail.ts | 2 +- .../config/dashboard/ha-config-navigation.js | 3 +- src/panels/config/ha-panel-config.js | 35 +-- .../config/person/dialog-person-detail.ts | 169 ++++++++++++++ src/panels/config/person/ha-config-person.ts | 217 ++++++++++++++++++ .../person/show-dialog-person-detail.ts | 23 ++ src/translations/en.json | 4 + 8 files changed, 484 insertions(+), 15 deletions(-) create mode 100644 src/data/person.ts create mode 100644 src/panels/config/person/dialog-person-detail.ts create mode 100644 src/panels/config/person/ha-config-person.ts create mode 100644 src/panels/config/person/show-dialog-person-detail.ts diff --git a/src/data/person.ts b/src/data/person.ts new file mode 100644 index 0000000000..00e77a838f --- /dev/null +++ b/src/data/person.ts @@ -0,0 +1,46 @@ +import { HomeAssistant } from "../types"; + +export interface Person { + id: string; + name: string; + user_id?: string; + device_trackers?: string[]; +} + +export interface PersonMutableParams { + name: string; + user_id: string | null; + device_trackers: string[]; +} + +export const fetchPersons = (hass: HomeAssistant) => + hass.callWS<{ + storage: Person[]; + config: Person[]; + }>({ type: "person/list" }); + +export const createPerson = ( + hass: HomeAssistant, + values: PersonMutableParams +) => + hass.callWS({ + type: "person/create", + ...values, + }); + +export const updatePerson = ( + hass: HomeAssistant, + personId: string, + updates: Partial +) => + hass.callWS({ + type: "person/update", + person_id: personId, + ...updates, + }); + +export const deletePerson = (hass: HomeAssistant, personId: string) => + hass.callWS({ + type: "person/delete", + person_id: personId, + }); diff --git a/src/panels/config/area_registry/show-dialog-area-registry-detail.ts b/src/panels/config/area_registry/show-dialog-area-registry-detail.ts index bbcc848b1c..c12b58b042 100644 --- a/src/panels/config/area_registry/show-dialog-area-registry-detail.ts +++ b/src/panels/config/area_registry/show-dialog-area-registry-detail.ts @@ -14,7 +14,7 @@ export interface AreaRegistryDetailDialogParams { } export const loadAreaRegistryDetailDialog = () => - import(/* webpackChunkName: "entity-registry-detail-dialog" */ "./dialog-area-registry-detail"); + import(/* webpackChunkName: "area-registry-detail-dialog" */ "./dialog-area-registry-detail"); export const showAreaRegistryDetailDialog = ( element: HTMLElement, diff --git a/src/panels/config/dashboard/ha-config-navigation.js b/src/panels/config/dashboard/ha-config-navigation.js index 3f337c0aa1..eb74814757 100644 --- a/src/panels/config/dashboard/ha-config-navigation.js +++ b/src/panels/config/dashboard/ha-config-navigation.js @@ -52,13 +52,14 @@ class HaConfigNavigation extends LocalizeMixin(NavigateMixin(PolymerElement)) { type: Array, value: [ "core", - "customize", + "person", "entity_registry", "area_registry", "automation", "script", "zha", "zwave", + "customize", ], }, }; diff --git a/src/panels/config/ha-panel-config.js b/src/panels/config/ha-panel-config.js index ed64ffd90f..8bfd4fdb3e 100644 --- a/src/panels/config/ha-panel-config.js +++ b/src/panels/config/ha-panel-config.js @@ -9,19 +9,6 @@ import isComponentLoaded from "../../common/config/is_component_loaded"; import EventsMixin from "../../mixins/events-mixin"; import NavigateMixin from "../../mixins/navigate-mixin"; -import(/* webpackChunkName: "panel-config-area-registry" */ "./area_registry/ha-config-area-registry"); -import(/* webpackChunkName: "panel-config-automation" */ "./automation/ha-config-automation"); -import(/* webpackChunkName: "panel-config-cloud" */ "./cloud/ha-config-cloud"); -import(/* webpackChunkName: "panel-config-config" */ "./config-entries/ha-config-entries"); -import(/* webpackChunkName: "panel-config-core" */ "./core/ha-config-core"); -import(/* webpackChunkName: "panel-config-customize" */ "./customize/ha-config-customize"); -import(/* webpackChunkName: "panel-config-dashboard" */ "./dashboard/ha-config-dashboard"); -import(/* webpackChunkName: "panel-config-script" */ "./script/ha-config-script"); -import(/* webpackChunkName: "panel-config-entity-registry" */ "./entity_registry/ha-config-entity-registry"); -import(/* webpackChunkName: "panel-config-users" */ "./users/ha-config-users"); -import(/* webpackChunkName: "panel-config-zha" */ "./zha/ha-config-zha"); -import(/* webpackChunkName: "panel-config-zwave" */ "./zwave/ha-config-zwave"); - /* * @appliesMixin EventsMixin * @appliesMixin NavigateMixin @@ -136,6 +123,15 @@ class HaPanelConfig extends EventsMixin(NavigateMixin(PolymerElement)) { > + +