Compare commits

..

74 Commits

Author SHA1 Message Date
Thomas Lovén
e87accdfb4 Arrayify actions list 2021-01-27 10:43:58 +01:00
Thomas Lovén
4dcfe3031d Allow multiple tap/hold/doubletap actions. 2021-01-10 19:29:29 +01:00
Bram Kragten
1538fbb102 Bump material elements (#8093) 2021-01-07 10:09:03 +01:00
Philip Allgaier
b9259b87eb Beautify ha-attribute <pre> (#8101) 2021-01-06 20:46:39 +01:00
David F. Mulcahey
30997dbc88 Add filtering and zoom to node to the ZHA network visualization (#7913) 2021-01-06 10:37:53 +01:00
Paulus Schoutsen
607eb6d130 Prefer target over environment (#8092) 2021-01-05 16:03:55 +01:00
Philip Allgaier
f2e9b3577d Use proper styled confirmation dialog for handled actions (#8077) 2021-01-05 14:04:45 +01:00
Philip Allgaier
cb2c6d8560 Convert gallery elements to LitElement (#8088) 2021-01-05 13:29:21 +01:00
dependabot[bot]
bfe8346ced Bump ini from 1.3.5 to 1.3.7 (#7949)
Bumps [ini](https://github.com/isaacs/ini) from 1.3.5 to 1.3.7.
- [Release notes](https://github.com/isaacs/ini/releases)
- [Commits](https://github.com/isaacs/ini/compare/v1.3.5...v1.3.7)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-01-05 11:47:56 +01:00
Siemon Geeroms
88da9bb91b Allow to show modal dialogs in iFrame panels (#7971) 2021-01-05 11:38:46 +01:00
Shane Qi
5e2ee1a16c Added Drag & Drop Reordering to Shopping List Card. (#7296) 2021-01-05 11:24:41 +01:00
Philip Allgaier
2fdc746392 Add support for "all" as entity ID in action "service" (#7954) 2021-01-05 11:02:47 +01:00
Philip Allgaier
1667973a66 Improve spacing in entity rows + secondary ellipsis (#8028) 2021-01-05 10:50:40 +01:00
Philip Allgaier
42f0101440 Add gallery demo for plant card (#7989) 2021-01-04 22:10:58 +01:00
Philip Allgaier
13b69bff1b Translate timestamp-display errors + tiny tweaks (#8086)
* Translate timestamp-display errors + tiny tweaks

* Adjust translation key
2021-01-04 20:55:43 +01:00
Philip Allgaier
2c2226dfd6 Slightly increase max attribute value width (#8085) 2021-01-04 19:51:30 +01:00
Philip Allgaier
a3fdfe0e15 Add additional entities to gallery more-info-light (#7930) 2021-01-04 18:06:47 +01:00
Philip Allgaier
a0de209a55 Aligned gallery more-info with hui-cards (#7931) 2021-01-04 18:05:30 +01:00
Philip Allgaier
0208b50ac7 Disable counter buttons if entity is unavailable (#8084) 2021-01-04 18:02:01 +01:00
Philip Allgaier
4a61779aba Ensure YAML editor gets updated during action change / deletion (#8024)
Co-authored-by: Bram Kragten <mail@bramkragten.nl>
2021-01-04 15:01:25 +01:00
Philip Allgaier
05057ade05 Catch navigator.clipboard errors (#7942)
Co-authored-by: Bram Kragten <mail@bramkragten.nl>
2021-01-04 14:04:54 +01:00
Abílio Costa
6fb206853c Switch header sizes for ZHA pairing status card (#8078)
Since the top title shows the result of the previous step ("Interview
Complete", "Configuration Complete", etc), while the second title shows
the current step ("Configuring", "Initializing", etc), having the top
title bigger would draw attention to it. So a user could see
"Configuration Complete" and assume pairing is done.

This change makes the second title bigger than the top one, so that the
user focus on the step that is in progress.
2021-01-04 14:03:12 +01:00
Charles Garwood
fab68055bf OZW Panel: Don't show an empty wakeup card (#8064) 2021-01-04 10:57:25 +01:00
Philip Allgaier
d5a77ef3cd Treat zero as String in number selector (#8070) 2021-01-04 10:53:40 +01:00
Philip Allgaier
dcb2605de4 Add Bengali language (#8014) 2021-01-04 10:28:25 +01:00
Philip Allgaier
e6d38f4539 Fix incorrect date selection ranges for history and logbook (#8045) 2021-01-04 10:04:14 +01:00
Philip Allgaier
293b56cfa6 Show actual error path/key in YAML card editor (#8076) 2021-01-04 09:58:07 +01:00
Philip Allgaier
775e93d54b Ensure consistent number display for gauge (#8080) 2021-01-04 09:24:51 +01:00
Philip Allgaier
7f840e75df Add EN fallback text for dismiss button (#8068) 2021-01-04 09:18:08 +01:00
Philip Allgaier
2113ea675e Prevent relative time text wrapping in more-info-sun (#8051) 2021-01-04 09:17:17 +01:00
Philip Allgaier
916a5c1a6b Make it clearer that we are looking for the YAML card config 2021-01-04 00:31:42 +01:00
HomeAssistant Azure
f684531315 [ci skip] Translation update 2021-01-02 00:41:18 +00:00
HomeAssistant Azure
fe8dda8996 [ci skip] Translation update 2020-12-31 00:32:48 +00:00
HomeAssistant Azure
4cd4b328c8 [ci skip] Translation update 2020-12-30 00:32:31 +00:00
Bram Kragten
d844c89b94 Merge branch 'master' into dev 2020-12-29 23:12:14 +01:00
Bram Kragten
177ea2b85a Bumped version to 20201229.0 2020-12-29 23:09:00 +01:00
Marc Mueller
50c5c15f49 Update group reload string (#7938)
* Update group reload string

* Update src/translations/en.json

Co-authored-by: Philip Allgaier <philip.allgaier@gmx.de>

* Update rest reload string

Co-authored-by: Philip Allgaier <philip.allgaier@gmx.de>
2020-12-29 23:03:44 +01:00
Philip Allgaier
1810760dc7 Mark entity ID as optional for button card (#7967) 2020-12-29 23:02:47 +01:00
Joakim Sørensen
4635b92e3f Fix add-on stage icon rendering (#7981) 2020-12-29 22:59:07 +01:00
Jochen Mehlhorn
1c652626eb Fix typo in hassio-supervisor-info.ts (#7907)
Fix typo in "unhealthy state" warning
2020-12-29 22:58:29 +01:00
Philip Allgaier
2000cfb1db Correct tabs for customizations page (#7990) 2020-12-29 22:52:58 +01:00
Philip Allgaier
f4d07828e7 Minor follow-up tweaks for PR 7947 (#7955) 2020-12-29 22:52:20 +01:00
Philip Allgaier
95b552671c Fix border radius for labeled slider (#7929) 2020-12-29 22:50:09 +01:00
Philip Allgaier
ef3bc3efe1 Do not render "No Area" in device table to reduce clutter (#7986) 2020-12-29 22:46:47 +01:00
Philip Allgaier
371ad899f5 Handle sorting with "undefined" (move always to bottom) (#7985) 2020-12-29 22:45:39 +01:00
plafü
2c54158d84 Fixes typo: 'bring to live'>'bring to life' (#8000) 2020-12-29 22:44:21 +01:00
quthla
5d9e30bbdc Fix translation of home state (#8015) 2020-12-29 22:43:41 +01:00
Marc Randolph
e477fd567d Button text is swapped on Lovelace raw YAML exit window (#8038)
Button text is swapped compared to the same thing elsewhere: d23165d06a/src/panels/lovelace/editor/card-editor/hui-dialog-edit-card.ts (L311)
2020-12-29 22:38:31 +01:00
Philip Allgaier
6a6c2937fe Ensure consistent spelling of "ID" (#8042) 2020-12-29 22:36:28 +01:00
HomeAssistant Azure
09e17c4da8 [ci skip] Translation update 2020-12-29 00:32:26 +00:00
HomeAssistant Azure
fd00469d11 [ci skip] Translation update 2020-12-28 00:32:37 +00:00
HomeAssistant Azure
cbbeb795f3 [ci skip] Translation update 2020-12-27 00:32:32 +00:00
HomeAssistant Azure
bba40e0da8 [ci skip] Translation update 2020-12-25 00:32:32 +00:00
HomeAssistant Azure
d23165d06a [ci skip] Translation update 2020-12-24 00:33:07 +00:00
HomeAssistant Azure
405fef6f03 [ci skip] Translation update 2020-12-23 00:32:49 +00:00
HomeAssistant Azure
588f217826 [ci skip] Translation update 2020-12-22 00:32:27 +00:00
HomeAssistant Azure
3d8b7cf80e [ci skip] Translation update 2020-12-21 00:32:26 +00:00
HomeAssistant Azure
c0ef923ad3 [ci skip] Translation update 2020-12-20 00:32:26 +00:00
HomeAssistant Azure
3df44fc71e [ci skip] Translation update 2020-12-19 00:33:33 +00:00
HomeAssistant Azure
c1965492d9 [ci skip] Translation update 2020-12-18 00:32:38 +00:00
HomeAssistant Azure
1f56ffde80 [ci skip] Translation update 2020-12-17 00:33:13 +00:00
Georgi Kirichkov
f335fdc002 Fixes a typo in hassio-supervisor-info.ts (#7987)
An "a" was missing in installtion
2020-12-16 10:28:21 +01:00
HomeAssistant Azure
0c914b5ec8 [ci skip] Translation update 2020-12-16 00:32:25 +00:00
Philip Allgaier
d767b06858 Fix spelling error (#7961)
Fixes https://github.com/home-assistant/frontend/issues/7958
2020-12-15 07:22:06 -06:00
Fabian Affolter
d4e49f3944 Update entry (#7978) 2020-12-15 09:49:26 +01:00
HomeAssistant Azure
7dfc5b3faf [ci skip] Translation update 2020-12-15 00:32:22 +00:00
HomeAssistant Azure
8a88033ab9 [ci skip] Translation update 2020-12-14 00:32:42 +00:00
HomeAssistant Azure
7b06b38c94 [ci skip] Translation update 2020-12-13 00:32:43 +00:00
Bram Kragten
5409752817 20201212.0 (#7952)
* [ci skip] Translation update

* Add link to the community forums to find more blueprints (#7947)

* Add link to the community forums to find more blueprints

* Apply suggestions from code review

Co-authored-by: Paulus Schoutsen <balloob@gmail.com>

Co-authored-by: Paulus Schoutsen <balloob@gmail.com>

* Fix `ha-relative-time` usage for tags and sun (#7944)

* Bumped version to 20201212.0

Co-authored-by: HomeAssistant Azure <hello@home-assistant.io>
Co-authored-by: Paulus Schoutsen <balloob@gmail.com>
Co-authored-by: Philip Allgaier <mail@spacegaier.de>
2020-12-12 20:57:28 +01:00
Bram Kragten
a70e6c49a1 Merge pull request #7940 from home-assistant/dev
20201210.0
2020-12-10 16:56:08 +01:00
Bram Kragten
c10dca9c7b Merge pull request #7910 from home-assistant/dev 2020-12-04 21:46:54 +01:00
Bram Kragten
2682c6e150 Merge pull request #7891 from home-assistant/dev 2020-12-03 17:22:08 +01:00
Bram Kragten
b7ccf3e0e5 Merge pull request #7875 from home-assistant/dev 2020-12-02 19:51:36 +01:00
Bram Kragten
50e7410002 Merge pull request #7833 from home-assistant/dev 2020-11-27 00:16:56 +01:00
133 changed files with 4579 additions and 2283 deletions

View File

@@ -74,12 +74,12 @@ DO NOT DELETE ANY TEXT from this template! Otherwise, your issue may be closed w
```
## Problem-relevant configuration
## Problem-relevant frontend configuration
<!--
An example configuration that caused the problem for you. Fill this out even
if it seems unimportant to you. Please be sure to remove personal information
like passwords, private URLs and other credentials.
An example configuration that caused the problem for you, e.g. the YAML configuration
of the used cards. Fill this out even if it seems unimportant to you. Please be sure
to remove personal information like passwords, private URLs and other credentials.
-->
```yaml
@@ -89,7 +89,7 @@ DO NOT DELETE ANY TEXT from this template! Otherwise, your issue may be closed w
## Javascript errors shown in your browser console/inspector
<!--
If you come across any javascript or other error logs, e.g., in your browser
If you come across any Javascript or other error logs, e.g. in your browser
console/inspector please provide them.
-->

View File

@@ -14,7 +14,7 @@ This is the repository for the official [Home Assistant](https://home-assistant.
- Development: [Instructions](https://developers.home-assistant.io/docs/frontend/development/)
- Production build: `script/build_frontend`
- Gallery: `cd gallery && script/develop_gallery`
- Hass.io: [Instructions](https://developers.home-assistant.io/docs/en/hassio_hass.html)
- Supervisor: [Instructions](https://developers.home-assistant.io/docs/supervisor/developing)
## Frontend development

View File

@@ -36,6 +36,7 @@ const createWebpackConfig = ({
const ignorePackages = bundle.ignorePackages({ latestBuild });
return {
mode: isProdBuild ? "production" : "development",
target: ["web", latestBuild ? "es2017" : "es5"],
devtool: isProdBuild
? "cheap-module-source-map"
: "eval-cheap-module-source-map",
@@ -131,22 +132,6 @@ const createWebpackConfig = ({
}
return `${chunk.name}.${chunk.hash.substr(0, 8)}.js`;
},
environment: {
// The environment supports arrow functions ('() => { ... }').
arrowFunction: latestBuild,
// The environment supports BigInt as literal (123n).
bigIntLiteral: false,
// The environment supports const and let for variable declarations.
const: latestBuild,
// The environment supports destructuring ('{ a, b } = obj').
destructuring: latestBuild,
// The environment supports an async import() function to import EcmaScript modules.
dynamicImport: latestBuild,
// The environment supports 'for of' iteration ('for (const x of array) { ... }').
forOf: latestBuild,
// The environment supports ECMAScript Module syntax to import ECMAScript modules (import ... from '...').
module: latestBuild,
},
chunkFilename:
isProdBuild && !isStatsBuild
? "chunk.[chunkhash].js"

View File

@@ -9,30 +9,29 @@ class DemoMoreInfo extends PolymerElement {
static get template() {
return html`
<style>
:host {
.root {
display: flex;
align-items: start;
}
#card {
max-width: 400px;
width: 100vw;
}
ha-card {
width: 333px;
width: 352px;
padding: 20px 24px;
}
state-card-content {
display: block;
margin-bottom: 16px;
}
pre {
width: 400px;
margin: 0 16px;
overflow: auto;
color: var(--primary-text-color);
}
@media only screen and (max-width: 800px) {
:host {
.root {
flex-direction: column;
}
pre {
@@ -40,21 +39,25 @@ class DemoMoreInfo extends PolymerElement {
}
}
</style>
<ha-card>
<state-card-content
state-obj="[[_stateObj]]"
hass="[[hass]]"
in-dialog
></state-card-content>
<div class="root">
<div id="card">
<ha-card>
<state-card-content
state-obj="[[_stateObj]]"
hass="[[hass]]"
in-dialog
></state-card-content>
<more-info-content
hass="[[hass]]"
state-obj="[[_stateObj]]"
></more-info-content>
</ha-card>
<template is="dom-if" if="[[showConfig]]">
<pre>[[_jsonEntity(_stateObj)]]</pre>
</template>
<more-info-content
hass="[[hass]]"
state-obj="[[_stateObj]]"
></more-info-content>
</ha-card>
</div>
<template is="dom-if" if="[[showConfig]]">
<pre>[[_jsonEntity(_stateObj)]]</pre>
</template>
</div>
`;
}

View File

@@ -3,12 +3,18 @@ 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-more-info";
import { applyThemesOnElement } from "../../../src/common/dom/apply_themes_on_element";
class DemoMoreInfos extends PolymerElement {
static get template() {
return html`
<style>
#container {
min-height: calc(100vh - 128px);
background: var(--primary-background-color);
}
.cards {
display: flex;
flex-wrap: wrap;
@@ -23,20 +29,31 @@ class DemoMoreInfos extends PolymerElement {
.filters {
margin-left: 60px;
}
ha-formfield {
margin-right: 16px;
}
</style>
<app-toolbar>
<div class="filters">
<ha-switch checked="{{_showConfig}}">Show entity</ha-switch>
<ha-formfield label="Show entities">
<ha-switch checked="[[_showConfig]]" on-change="_showConfigToggled">
</ha-switch>
</ha-formfield>
<ha-formfield label="Dark theme">
<ha-switch on-change="_darkThemeToggled"> </ha-switch>
</ha-formfield>
</div>
</app-toolbar>
<div class="cards">
<template is="dom-repeat" items="[[entities]]">
<demo-more-info
entity-id="[[item]]"
show-config="[[_showConfig]]"
hass="[[hass]]"
></demo-more-info>
</template>
<div id="container">
<div class="cards">
<template is="dom-repeat" items="[[entities]]">
<demo-more-info
entity-id="[[item]]"
show-config="[[_showConfig]]"
hass="[[hass]]"
></demo-more-info>
</template>
</div>
</div>
`;
}
@@ -51,6 +68,16 @@ class DemoMoreInfos extends PolymerElement {
},
};
}
_showConfigToggled(ev) {
this._showConfig = ev.target.checked;
}
_darkThemeToggled(ev) {
applyThemesOnElement(this.$.container, { themes: {} }, "default", {
dark: ev.target.checked,
});
}
}
customElements.define("demo-more-infos", DemoMoreInfos);

View File

@@ -0,0 +1,72 @@
import { getEntity } from "../../../src/fake_data/entity";
export const createPlantEntities = () => [
getEntity("plant", "lemon_tree", "ok", {
problem: "none",
sensors: {
moisture: "sensor.lemon_tree_moisture",
battery: "sensor.lemon_tree_battery",
temperature: "sensor.lemon_tree_temperature",
conductivity: "sensor.lemon_tree_conductivity",
brightness: "sensor.lemon_tree_brightness",
},
unit_of_measurement_dict: {
temperature: "°C",
moisture: "%",
brightness: "lx",
battery: "%",
conductivity: "μS/cm",
},
moisture: 54,
battery: 95,
temperature: 15.6,
conductivity: 1,
brightness: 12,
max_brightness: 20,
friendly_name: "Lemon Tree",
}),
getEntity("plant", "apple_tree", "ok", {
problem: "brightness",
sensors: {
moisture: "sensor.apple_tree_moisture",
battery: "sensor.apple_tree_battery",
temperature: "sensor.apple_tree_temperature",
conductivity: "sensor.apple_tree_conductivity",
brightness: "sensor.apple_tree_brightness",
},
unit_of_measurement_dict: {
temperature: "°C",
moisture: "%",
brightness: "lx",
battery: "%",
conductivity: "μS/cm",
},
moisture: 54,
battery: 2,
temperature: 15.6,
conductivity: 1,
brightness: 25,
max_brightness: 20,
friendly_name: "Apple Tree",
}),
getEntity("plant", "sunflowers", "ok", {
problem: "moisture, temperature, conductivity",
sensors: {
moisture: "sensor.sunflowers_moisture",
temperature: "sensor.sunflowers_temperature",
conductivity: "sensor.sunflowers_conductivity",
brightness: "sensor.sunflowers_brightness",
},
unit_of_measurement_dict: {
temperature: "°C",
moisture: "%",
brightness: "lx",
conductivity: "μS/cm",
},
moisture: 54,
temperature: 15.6,
conductivity: 1,
brightness: 25,
entity_picture: "/images/sunflowers.jpg",
}),
];

View File

@@ -1,6 +1,11 @@
import { html } from "@polymer/polymer/lib/utils/html-tag";
/* eslint-plugin-disable lit */
import { PolymerElement } from "@polymer/polymer/polymer-element";
import {
html,
LitElement,
customElement,
PropertyValues,
query,
TemplateResult,
} from "lit-element";
import { getEntity } from "../../../src/fake_data/entity";
import { provideHass } from "../../../src/fake_data/provide_hass";
import "../components/demo-cards";
@@ -71,28 +76,19 @@ const CONFIGS = [
},
];
class DemoAlarmPanelEntity extends PolymerElement {
static get template() {
return html` <demo-cards id="demos" configs="[[_configs]]"></demo-cards> `;
@customElement("demo-hui-alarm-panel-card")
class DemoAlarmPanelEntity extends LitElement {
@query("#demos") private _demoRoot!: HTMLElement;
protected render(): TemplateResult {
return html`<demo-cards id="demos" .configs=${CONFIGS}></demo-cards>`;
}
static get properties() {
return {
_configs: {
type: Object,
value: CONFIGS,
},
};
}
public ready() {
super.ready();
this._setupDemo();
}
private async _setupDemo() {
const hass = provideHass(this.$.demos);
await hass.updateTranslations(null, "en");
protected firstUpdated(changedProperties: PropertyValues) {
super.firstUpdated(changedProperties);
const hass = provideHass(this._demoRoot);
hass.updateTranslations(null, "en");
hass.updateTranslations("lovelace", "en");
hass.addEntities(ENTITIES);
}
}

View File

@@ -1,6 +1,11 @@
import { html } from "@polymer/polymer/lib/utils/html-tag";
/* eslint-plugin-disable lit */
import { PolymerElement } from "@polymer/polymer/polymer-element";
import {
html,
LitElement,
customElement,
PropertyValues,
query,
TemplateResult,
} from "lit-element";
import { getEntity } from "../../../src/fake_data/entity";
import { provideHass } from "../../../src/fake_data/provide_hass";
import "../components/demo-cards";
@@ -53,24 +58,19 @@ const CONFIGS = [
},
];
class DemoConditional extends PolymerElement {
static get template() {
return html` <demo-cards id="demos" configs="[[_configs]]"></demo-cards> `;
@customElement("demo-hui-conditional-card")
class DemoConditional extends LitElement {
@query("#demos") private _demoRoot!: HTMLElement;
protected render(): TemplateResult {
return html`<demo-cards id="demos" .configs=${CONFIGS}></demo-cards>`;
}
static get properties() {
return {
_configs: {
type: Object,
value: CONFIGS,
},
};
}
public ready() {
super.ready();
const hass = provideHass(this.$.demos);
protected firstUpdated(changedProperties: PropertyValues) {
super.firstUpdated(changedProperties);
const hass = provideHass(this._demoRoot);
hass.updateTranslations(null, "en");
hass.updateTranslations("lovelace", "en");
hass.addEntities(ENTITIES);
}
}

View File

@@ -1,6 +1,11 @@
import { html } from "@polymer/polymer/lib/utils/html-tag";
/* eslint-plugin-disable lit */
import { PolymerElement } from "@polymer/polymer/polymer-element";
import {
html,
LitElement,
customElement,
PropertyValues,
query,
TemplateResult,
} from "lit-element";
import { getEntity } from "../../../src/fake_data/entity";
import { provideHass } from "../../../src/fake_data/provide_hass";
import "../components/demo-cards";
@@ -217,24 +222,19 @@ const CONFIGS = [
},
];
class DemoEntities extends PolymerElement {
static get template() {
return html` <demo-cards id="demos" configs="[[_configs]]"></demo-cards> `;
@customElement("demo-hui-entities-card")
class DemoEntities extends LitElement {
@query("#demos") private _demoRoot!: HTMLElement;
protected render(): TemplateResult {
return html`<demo-cards id="demos" .configs=${CONFIGS}></demo-cards>`;
}
static get properties() {
return {
_configs: {
type: Object,
value: CONFIGS,
},
};
}
public ready() {
super.ready();
const hass = provideHass(this.$.demos);
protected firstUpdated(changedProperties: PropertyValues) {
super.firstUpdated(changedProperties);
const hass = provideHass(this._demoRoot);
hass.updateTranslations(null, "en");
hass.updateTranslations("lovelace", "en");
hass.addEntities(ENTITIES);
}
}

View File

@@ -1,6 +1,11 @@
import { html } from "@polymer/polymer/lib/utils/html-tag";
/* eslint-plugin-disable lit */
import { PolymerElement } from "@polymer/polymer/polymer-element";
import {
html,
LitElement,
customElement,
PropertyValues,
query,
TemplateResult,
} from "lit-element";
import { getEntity } from "../../../src/fake_data/entity";
import { provideHass } from "../../../src/fake_data/provide_hass";
import "../components/demo-cards";
@@ -69,24 +74,19 @@ const CONFIGS = [
},
];
class DemoButtonEntity extends PolymerElement {
static get template() {
return html` <demo-cards id="demos" configs="[[_configs]]"></demo-cards> `;
@customElement("demo-hui-entity-button-card")
class DemoButtonEntity extends LitElement {
@query("#demos") private _demoRoot!: HTMLElement;
protected render(): TemplateResult {
return html`<demo-cards id="demos" .configs=${CONFIGS}></demo-cards>`;
}
static get properties() {
return {
_configs: {
type: Object,
value: CONFIGS,
},
};
}
public ready() {
super.ready();
const hass = provideHass(this.$.demos);
protected firstUpdated(changedProperties: PropertyValues) {
super.firstUpdated(changedProperties);
const hass = provideHass(this._demoRoot);
hass.updateTranslations(null, "en");
hass.updateTranslations("lovelace", "en");
hass.addEntities(ENTITIES);
}
}

View File

@@ -1,6 +1,11 @@
import { html } from "@polymer/polymer/lib/utils/html-tag";
/* eslint-plugin-disable lit */
import { PolymerElement } from "@polymer/polymer/polymer-element";
import {
html,
LitElement,
customElement,
PropertyValues,
query,
TemplateResult,
} from "lit-element";
import { getEntity } from "../../../src/fake_data/entity";
import { provideHass } from "../../../src/fake_data/provide_hass";
import "../components/demo-cards";
@@ -89,26 +94,21 @@ const CONFIGS = [
},
];
class DemoFilter extends PolymerElement {
static get template() {
return html` <demo-cards id="demos" configs="[[_configs]]"></demo-cards> `;
@customElement("demo-hui-entity-filter-card")
class DemoEntityFilter extends LitElement {
@query("#demos") private _demoRoot!: HTMLElement;
protected render(): TemplateResult {
return html`<demo-cards id="demos" .configs=${CONFIGS}></demo-cards>`;
}
static get properties() {
return {
_configs: {
type: Object,
value: CONFIGS,
},
};
}
public ready() {
super.ready();
const hass = provideHass(this.$.demos);
protected firstUpdated(changedProperties: PropertyValues) {
super.firstUpdated(changedProperties);
const hass = provideHass(this._demoRoot);
hass.updateTranslations(null, "en");
hass.updateTranslations("lovelace", "en");
hass.addEntities(ENTITIES);
}
}
customElements.define("demo-hui-entity-filter-card", DemoFilter);
customElements.define("demo-hui-entity-filter-card", DemoEntityFilter);

View File

@@ -1,6 +1,11 @@
import { html } from "@polymer/polymer/lib/utils/html-tag";
/* eslint-plugin-disable lit */
import { PolymerElement } from "@polymer/polymer/polymer-element";
import {
html,
LitElement,
customElement,
PropertyValues,
query,
TemplateResult,
} from "lit-element";
import { getEntity } from "../../../src/fake_data/entity";
import { provideHass } from "../../../src/fake_data/provide_hass";
import "../components/demo-cards";
@@ -107,24 +112,19 @@ const CONFIGS = [
},
];
class DemoGaugeEntity extends PolymerElement {
static get template() {
return html` <demo-cards id="demos" configs="[[_configs]]"></demo-cards> `;
@customElement("demo-hui-gauge-card")
class DemoGaugeEntity extends LitElement {
@query("#demos") private _demoRoot!: HTMLElement;
protected render(): TemplateResult {
return html`<demo-cards id="demos" .configs=${CONFIGS}></demo-cards>`;
}
static get properties() {
return {
_configs: {
type: Object,
value: CONFIGS,
},
};
}
public ready() {
super.ready();
const hass = provideHass(this.$.demos);
protected firstUpdated(changedProperties: PropertyValues) {
super.firstUpdated(changedProperties);
const hass = provideHass(this._demoRoot);
hass.updateTranslations(null, "en");
hass.updateTranslations("lovelace", "en");
hass.addEntities(ENTITIES);
}
}

View File

@@ -1,6 +1,11 @@
import { html } from "@polymer/polymer/lib/utils/html-tag";
/* eslint-plugin-disable lit */
import { PolymerElement } from "@polymer/polymer/polymer-element";
import {
html,
LitElement,
customElement,
PropertyValues,
query,
TemplateResult,
} from "lit-element";
import { getEntity } from "../../../src/fake_data/entity";
import { provideHass } from "../../../src/fake_data/provide_hass";
import "../components/demo-cards";
@@ -218,26 +223,21 @@ const CONFIGS = [
},
];
class DemoPicEntity extends PolymerElement {
static get template() {
return html` <demo-cards id="demos" configs="[[_configs]]"></demo-cards> `;
@customElement("demo-hui-glance-card")
class DemoGlanceEntity extends LitElement {
@query("#demos") private _demoRoot!: HTMLElement;
protected render(): TemplateResult {
return html`<demo-cards id="demos" .configs=${CONFIGS}></demo-cards>`;
}
static get properties() {
return {
_configs: {
type: Object,
value: CONFIGS,
},
};
}
public ready() {
super.ready();
const hass = provideHass(this.$.demos);
protected firstUpdated(changedProperties: PropertyValues) {
super.firstUpdated(changedProperties);
const hass = provideHass(this._demoRoot);
hass.updateTranslations(null, "en");
hass.updateTranslations("lovelace", "en");
hass.addEntities(ENTITIES);
}
}
customElements.define("demo-hui-glance-card", DemoPicEntity);
customElements.define("demo-hui-glance-card", DemoGlanceEntity);

View File

@@ -1,6 +1,4 @@
import { html } from "@polymer/polymer/lib/utils/html-tag";
/* eslint-plugin-disable lit */
import { PolymerElement } from "@polymer/polymer/polymer-element";
import { html, LitElement, customElement, TemplateResult } from "lit-element";
import "../components/demo-cards";
const CONFIGS = [
@@ -37,18 +35,10 @@ const CONFIGS = [
},
];
class DemoIframe extends PolymerElement {
static get template() {
return html` <demo-cards configs="[[_configs]]"></demo-cards> `;
}
static get properties() {
return {
_configs: {
type: Object,
value: CONFIGS,
},
};
@customElement("demo-hui-iframe-card")
class DemoIframe extends LitElement {
protected render(): TemplateResult {
return html`<demo-cards id="demos" .configs=${CONFIGS}></demo-cards>`;
}
}

View File

@@ -1,6 +1,11 @@
import { html } from "@polymer/polymer/lib/utils/html-tag";
/* eslint-plugin-disable lit */
import { PolymerElement } from "@polymer/polymer/polymer-element";
import {
html,
LitElement,
customElement,
PropertyValues,
query,
TemplateResult,
} from "lit-element";
import { getEntity } from "../../../src/fake_data/entity";
import { provideHass } from "../../../src/fake_data/provide_hass";
import "../components/demo-cards";
@@ -63,24 +68,19 @@ const CONFIGS = [
},
];
class DemoLightEntity extends PolymerElement {
static get template() {
return html` <demo-cards id="demos" configs="[[_configs]]"></demo-cards> `;
@customElement("demo-hui-light-card")
class DemoLightEntity extends LitElement {
@query("#demos") private _demoRoot!: HTMLElement;
protected render(): TemplateResult {
return html`<demo-cards id="demos" .configs=${CONFIGS}></demo-cards>`;
}
static get properties() {
return {
_configs: {
type: Object,
value: CONFIGS,
},
};
}
public ready() {
super.ready();
const hass = provideHass(this.$.demos);
protected firstUpdated(changedProperties: PropertyValues) {
super.firstUpdated(changedProperties);
const hass = provideHass(this._demoRoot);
hass.updateTranslations(null, "en");
hass.updateTranslations("lovelace", "en");
hass.addEntities(ENTITIES);
}
}

View File

@@ -1,6 +1,11 @@
import { html } from "@polymer/polymer/lib/utils/html-tag";
/* eslint-plugin-disable lit */
import { PolymerElement } from "@polymer/polymer/polymer-element";
import {
html,
LitElement,
customElement,
PropertyValues,
query,
TemplateResult,
} from "lit-element";
import { getEntity } from "../../../src/fake_data/entity";
import { provideHass } from "../../../src/fake_data/provide_hass";
import "../components/demo-cards";
@@ -161,24 +166,19 @@ const CONFIGS = [
},
];
class DemoMap extends PolymerElement {
static get template() {
return html` <demo-cards id="demos" configs="[[_configs]]"></demo-cards> `;
@customElement("demo-hui-map-card")
class DemoMap extends LitElement {
@query("#demos") private _demoRoot!: HTMLElement;
protected render(): TemplateResult {
return html`<demo-cards id="demos" .configs=${CONFIGS}></demo-cards>`;
}
static get properties() {
return {
_configs: {
type: Object,
value: CONFIGS,
},
};
}
public ready() {
super.ready();
const hass = provideHass(this.$.demos);
protected firstUpdated(changedProperties: PropertyValues) {
super.firstUpdated(changedProperties);
const hass = provideHass(this._demoRoot);
hass.updateTranslations(null, "en");
hass.updateTranslations("lovelace", "en");
hass.addEntities(ENTITIES);
}
}

View File

@@ -1,6 +1,11 @@
import { html } from "@polymer/polymer/lib/utils/html-tag";
/* eslint-plugin-disable lit */
import { PolymerElement } from "@polymer/polymer/polymer-element";
import {
html,
LitElement,
customElement,
PropertyValues,
query,
TemplateResult,
} from "lit-element";
import { mockTemplate } from "../../../demo/src/stubs/template";
import { provideHass } from "../../../src/fake_data/provide_hass";
import "../components/demo-cards";
@@ -254,23 +259,19 @@ const CONFIGS = [
},
];
class DemoMarkdown extends PolymerElement {
static get template() {
return html` <demo-cards id="demos" configs="[[_configs]]"></demo-cards> `;
@customElement("demo-hui-markdown-card")
class DemoMarkdown extends LitElement {
@query("#demos") private _demoRoot!: HTMLElement;
protected render(): TemplateResult {
return html`<demo-cards id="demos" .configs=${CONFIGS}></demo-cards>`;
}
static get properties() {
return {
_configs: {
type: Object,
value: CONFIGS,
},
};
}
public ready() {
super.ready();
const hass = provideHass(this.$.demos);
protected firstUpdated(changedProperties: PropertyValues) {
super.firstUpdated(changedProperties);
const hass = provideHass(this._demoRoot);
hass.updateTranslations(null, "en");
hass.updateTranslations("lovelace", "en");
mockTemplate(hass);
}
}

View File

@@ -1,6 +1,11 @@
import { html } from "@polymer/polymer/lib/utils/html-tag";
/* eslint-plugin-disable lit */
import { PolymerElement } from "@polymer/polymer/polymer-element";
import {
html,
LitElement,
customElement,
PropertyValues,
query,
TemplateResult,
} from "lit-element";
import { provideHass } from "../../../src/fake_data/provide_hass";
import "../components/demo-cards";
import { createMediaPlayerEntities } from "../data/media_players";
@@ -158,26 +163,21 @@ const CONFIGS = [
},
];
class DemoHuiMediControlCard extends PolymerElement {
static get template() {
return html` <demo-cards id="demos" configs="[[_configs]]"></demo-cards> `;
@customElement("demo-hui-media-control-card")
class DemoHuiMediaControlCard extends LitElement {
@query("#demos") private _demoRoot!: HTMLElement;
protected render(): TemplateResult {
return html`<demo-cards id="demos" .configs=${CONFIGS}></demo-cards>`;
}
static get properties() {
return {
_configs: {
type: Object,
value: CONFIGS,
},
};
}
public ready() {
super.ready();
const hass = provideHass(this.$.demos);
protected firstUpdated(changedProperties: PropertyValues) {
super.firstUpdated(changedProperties);
const hass = provideHass(this._demoRoot);
hass.updateTranslations(null, "en");
hass.updateTranslations("lovelace", "en");
hass.addEntities(createMediaPlayerEntities());
}
}
customElements.define("demo-hui-media-control-card", DemoHuiMediControlCard);
customElements.define("demo-hui-media-control-card", DemoHuiMediaControlCard);

View File

@@ -1,6 +1,11 @@
import { html } from "@polymer/polymer/lib/utils/html-tag";
/* eslint-plugin-disable lit */
import { PolymerElement } from "@polymer/polymer/polymer-element";
import {
html,
LitElement,
customElement,
PropertyValues,
query,
TemplateResult,
} from "lit-element";
import { provideHass } from "../../../src/fake_data/provide_hass";
import "../components/demo-cards";
import { createMediaPlayerEntities } from "../data/media_players";
@@ -55,26 +60,21 @@ const CONFIGS = [
},
];
class DemoHuiMediaPlayerRows extends PolymerElement {
static get template() {
return html` <demo-cards id="demos" configs="[[_configs]]"></demo-cards> `;
@customElement("demo-hui-media-player-row")
class DemoHuiMediaPlayerRow extends LitElement {
@query("#demos") private _demoRoot!: HTMLElement;
protected render(): TemplateResult {
return html`<demo-cards id="demos" .configs=${CONFIGS}></demo-cards>`;
}
static get properties() {
return {
_configs: {
type: Object,
value: CONFIGS,
},
};
}
public ready() {
super.ready();
const hass = provideHass(this.$.demos);
protected firstUpdated(changedProperties: PropertyValues) {
super.firstUpdated(changedProperties);
const hass = provideHass(this._demoRoot);
hass.updateTranslations(null, "en");
hass.updateTranslations("lovelace", "en");
hass.addEntities(createMediaPlayerEntities());
}
}
customElements.define("demo-hui-media-player-rows", DemoHuiMediaPlayerRows);
customElements.define("demo-hui-media-player-row", DemoHuiMediaPlayerRow);

View File

@@ -1,6 +1,11 @@
import { html } from "@polymer/polymer/lib/utils/html-tag";
/* eslint-plugin-disable lit */
import { PolymerElement } from "@polymer/polymer/polymer-element";
import {
html,
LitElement,
customElement,
PropertyValues,
query,
TemplateResult,
} from "lit-element";
import { getEntity } from "../../../src/fake_data/entity";
import { provideHass } from "../../../src/fake_data/provide_hass";
import "../components/demo-cards";
@@ -125,26 +130,21 @@ const CONFIGS = [
},
];
class DemoPicElements extends PolymerElement {
static get template() {
return html` <demo-cards id="demos" configs="[[_configs]]"></demo-cards> `;
@customElement("demo-hui-picture-elements-card")
class DemoPictureElements extends LitElement {
@query("#demos") private _demoRoot!: HTMLElement;
protected render(): TemplateResult {
return html`<demo-cards id="demos" .configs=${CONFIGS}></demo-cards>`;
}
static get properties() {
return {
_configs: {
type: Object,
value: CONFIGS,
},
};
}
public ready() {
super.ready();
const hass = provideHass(this.$.demos);
protected firstUpdated(changedProperties: PropertyValues) {
super.firstUpdated(changedProperties);
const hass = provideHass(this._demoRoot);
hass.updateTranslations(null, "en");
hass.updateTranslations("lovelace", "en");
hass.addEntities(ENTITIES);
}
}
customElements.define("demo-hui-picture-elements-card", DemoPicElements);
customElements.define("demo-hui-picture-elements-card", DemoPictureElements);

View File

@@ -1,6 +1,11 @@
import { html } from "@polymer/polymer/lib/utils/html-tag";
/* eslint-plugin-disable lit */
import { PolymerElement } from "@polymer/polymer/polymer-element";
import {
html,
LitElement,
customElement,
PropertyValues,
query,
TemplateResult,
} from "lit-element";
import { getEntity } from "../../../src/fake_data/entity";
import { provideHass } from "../../../src/fake_data/provide_hass";
import "../components/demo-cards";
@@ -80,26 +85,21 @@ const CONFIGS = [
},
];
class DemoPicEntity extends PolymerElement {
static get template() {
return html` <demo-cards id="demos" configs="[[_configs]]"></demo-cards> `;
@customElement("demo-hui-picture-entity-card")
class DemoPictureEntity extends LitElement {
@query("#demos") private _demoRoot!: HTMLElement;
protected render(): TemplateResult {
return html`<demo-cards id="demos" .configs=${CONFIGS}></demo-cards>`;
}
static get properties() {
return {
_configs: {
type: Object,
value: CONFIGS,
},
};
}
public ready() {
super.ready();
const hass = provideHass(this.$.demos);
protected firstUpdated(changedProperties: PropertyValues) {
super.firstUpdated(changedProperties);
const hass = provideHass(this._demoRoot);
hass.updateTranslations(null, "en");
hass.updateTranslations("lovelace", "en");
hass.addEntities(ENTITIES);
}
}
customElements.define("demo-hui-picture-entity-card", DemoPicEntity);
customElements.define("demo-hui-picture-entity-card", DemoPictureEntity);

View File

@@ -1,6 +1,11 @@
import { html } from "@polymer/polymer/lib/utils/html-tag";
/* eslint-plugin-disable lit */
import { PolymerElement } from "@polymer/polymer/polymer-element";
import {
html,
LitElement,
customElement,
PropertyValues,
query,
TemplateResult,
} from "lit-element";
import { getEntity } from "../../../src/fake_data/entity";
import { provideHass } from "../../../src/fake_data/provide_hass";
import "../components/demo-cards";
@@ -121,26 +126,21 @@ const CONFIGS = [
},
];
class DemoPicGlance extends PolymerElement {
static get template() {
return html` <demo-cards id="demos" configs="[[_configs]]"></demo-cards> `;
@customElement("demo-hui-picture-glance-card")
class DemoPictureGlance extends LitElement {
@query("#demos") private _demoRoot!: HTMLElement;
protected render(): TemplateResult {
return html`<demo-cards id="demos" .configs=${CONFIGS}></demo-cards>`;
}
static get properties() {
return {
_configs: {
type: Object,
value: CONFIGS,
},
};
}
public ready() {
super.ready();
const hass = provideHass(this.$.demos);
protected firstUpdated(changedProperties: PropertyValues) {
super.firstUpdated(changedProperties);
const hass = provideHass(this._demoRoot);
hass.updateTranslations(null, "en");
hass.updateTranslations("lovelace", "en");
hass.addEntities(ENTITIES);
}
}
customElements.define("demo-hui-picture-glance-card", DemoPicGlance);
customElements.define("demo-hui-picture-glance-card", DemoPictureGlance);

View File

@@ -0,0 +1,55 @@
import {
html,
LitElement,
customElement,
PropertyValues,
query,
TemplateResult,
} from "lit-element";
import { provideHass } from "../../../src/fake_data/provide_hass";
import "../components/demo-cards";
import { createPlantEntities } from "../data/plants";
const CONFIGS = [
{
heading: "Basic example",
config: `
- type: plant-status
entity: plant.lemon_tree
`,
},
{
heading: "Problem (too bright) + low battery",
config: `
- type: plant-status
entity: plant.apple_tree
`,
},
{
heading: "With picture + multiple problems",
config: `
- type: plant-status
entity: plant.sunflowers
name: Sunflowers Name Overwrite
`,
},
];
@customElement("demo-hui-plant-card")
export class DemoPlantEntity extends LitElement {
@query("#demos") private _demoRoot!: HTMLElement;
protected render(): TemplateResult {
return html`<demo-cards id="demos" .configs=${CONFIGS}></demo-cards>`;
}
protected firstUpdated(changedProperties: PropertyValues) {
super.firstUpdated(changedProperties);
const hass = provideHass(this._demoRoot);
hass.updateTranslations(null, "en");
hass.updateTranslations("lovelace", "en");
hass.addEntities(createPlantEntities());
}
}
customElements.define("demo-hui-plant-card", DemoPlantEntity);

View File

@@ -1,6 +1,11 @@
import { html } from "@polymer/polymer/lib/utils/html-tag";
/* eslint-plugin-disable lit */
import { PolymerElement } from "@polymer/polymer/polymer-element";
import {
html,
LitElement,
customElement,
PropertyValues,
query,
TemplateResult,
} from "lit-element";
import { provideHass } from "../../../src/fake_data/provide_hass";
import "../components/demo-cards";
@@ -20,24 +25,19 @@ const CONFIGS = [
},
];
class DemoShoppingListEntity extends PolymerElement {
static get template() {
return html` <demo-cards id="demos" configs="[[_configs]]"></demo-cards> `;
@customElement("demo-hui-shopping-list-card")
class DemoShoppingListEntity extends LitElement {
@query("#demos") private _demoRoot!: HTMLElement;
protected render(): TemplateResult {
return html`<demo-cards id="demos" .configs=${CONFIGS}></demo-cards>`;
}
static get properties() {
return {
_configs: {
type: Object,
value: CONFIGS,
},
};
}
public ready() {
super.ready();
const hass = provideHass(this.$.demos);
protected firstUpdated(changedProperties: PropertyValues) {
super.firstUpdated(changedProperties);
const hass = provideHass(this._demoRoot);
hass.updateTranslations(null, "en");
hass.updateTranslations("lovelace", "en");
hass.mockAPI("shopping_list", () => [
{ name: "list", id: 1, complete: false },

View File

@@ -1,6 +1,11 @@
import { html } from "@polymer/polymer/lib/utils/html-tag";
/* eslint-plugin-disable lit */
import { PolymerElement } from "@polymer/polymer/polymer-element";
import {
html,
LitElement,
customElement,
PropertyValues,
query,
TemplateResult,
} from "lit-element";
import { mockHistory } from "../../../demo/src/stubs/history";
import { getEntity } from "../../../src/fake_data/entity";
import { provideHass } from "../../../src/fake_data/provide_hass";
@@ -132,24 +137,19 @@ const CONFIGS = [
},
];
class DemoStack extends PolymerElement {
static get template() {
return html` <demo-cards id="demos" configs="[[_configs]]"></demo-cards> `;
@customElement("demo-hui-stack-card")
class DemoStack extends LitElement {
@query("#demos") private _demoRoot!: HTMLElement;
protected render(): TemplateResult {
return html`<demo-cards id="demos" .configs=${CONFIGS}></demo-cards>`;
}
static get properties() {
return {
_configs: {
type: Object,
value: CONFIGS,
},
};
}
public ready() {
super.ready();
const hass = provideHass(this.$.demos);
protected firstUpdated(changedProperties: PropertyValues) {
super.firstUpdated(changedProperties);
const hass = provideHass(this._demoRoot);
hass.updateTranslations(null, "en");
hass.updateTranslations("lovelace", "en");
hass.addEntities(ENTITIES);
mockHistory(hass);
}

View File

@@ -1,6 +1,11 @@
import { html } from "@polymer/polymer/lib/utils/html-tag";
/* eslint-plugin-disable lit */
import { PolymerElement } from "@polymer/polymer/polymer-element";
import {
html,
LitElement,
customElement,
PropertyValues,
query,
TemplateResult,
} from "lit-element";
import { getEntity } from "../../../src/fake_data/entity";
import { provideHass } from "../../../src/fake_data/provide_hass";
import "../components/demo-cards";
@@ -74,24 +79,19 @@ const CONFIGS = [
},
];
class DemoThermostatEntity extends PolymerElement {
static get template() {
return html` <demo-cards id="demos" configs="[[_configs]]"></demo-cards> `;
@customElement("demo-hui-thermostat-card")
class DemoThermostatEntity extends LitElement {
@query("#demos") private _demoRoot!: HTMLElement;
protected render(): TemplateResult {
return html`<demo-cards id="demos" .configs=${CONFIGS}></demo-cards>`;
}
static get properties() {
return {
_configs: {
type: Object,
value: CONFIGS,
},
};
}
public ready() {
super.ready();
const hass = provideHass(this.$.demos);
protected firstUpdated(changedProperties: PropertyValues) {
super.firstUpdated(changedProperties);
const hass = provideHass(this._demoRoot);
hass.updateTranslations(null, "en");
hass.updateTranslations("lovelace", "en");
hass.addEntities(ENTITIES);
}
}

View File

@@ -1,10 +1,27 @@
import { html } from "@polymer/polymer/lib/utils/html-tag";
/* eslint-plugin-disable lit */
import { PolymerElement } from "@polymer/polymer/polymer-element";
import {
html,
LitElement,
customElement,
property,
PropertyValues,
query,
TemplateResult,
} from "lit-element";
import "../../../src/components/ha-card";
import { SUPPORT_BRIGHTNESS } from "../../../src/data/light";
import {
SUPPORT_BRIGHTNESS,
SUPPORT_COLOR_TEMP,
SUPPORT_EFFECT,
SUPPORT_FLASH,
SUPPORT_COLOR,
SUPPORT_TRANSITION,
SUPPORT_WHITE_VALUE,
} from "../../../src/data/light";
import { getEntity } from "../../../src/fake_data/entity";
import { provideHass } from "../../../src/fake_data/provide_hass";
import {
provideHass,
MockHomeAssistant,
} from "../../../src/fake_data/provide_hass";
import "../components/demo-more-infos";
import "../../../src/dialogs/more-info/more-info-content";
@@ -14,38 +31,52 @@ const ENTITIES = [
}),
getEntity("light", "kitchen_light", "on", {
friendly_name: "Brightness Light",
brightness: 80,
brightness: 200,
supported_features: SUPPORT_BRIGHTNESS,
}),
getEntity("light", "color_temperature_light", "on", {
friendly_name: "White Color Temperature Light",
brightness: 128,
color_temp: 75,
min_mireds: 30,
max_mireds: 150,
supported_features: SUPPORT_BRIGHTNESS + SUPPORT_COLOR_TEMP,
}),
getEntity("light", "color_effectslight", "on", {
friendly_name: "Color Effets Light",
brightness: 255,
hs_color: [30, 100],
white_value: 36,
supported_features:
SUPPORT_BRIGHTNESS +
SUPPORT_EFFECT +
SUPPORT_FLASH +
SUPPORT_COLOR +
SUPPORT_TRANSITION +
SUPPORT_WHITE_VALUE,
effect_list: ["random", "colorloop"],
}),
];
class DemoMoreInfoLight extends PolymerElement {
static get template() {
@customElement("demo-more-info-light")
class DemoMoreInfoLight extends LitElement {
@property() public hass!: MockHomeAssistant;
@query("demo-more-infos") private _demoRoot!: HTMLElement;
protected render(): TemplateResult {
return html`
<demo-more-infos
hass="[[hass]]"
entities="[[_entities]]"
.hass=${this.hass}
.entities=${ENTITIES.map((ent) => ent.entityId)}
></demo-more-infos>
`;
}
static get properties() {
return {
_entities: {
type: Array,
value: ENTITIES.map((ent) => ent.entityId),
},
};
}
public ready() {
super.ready();
this._setupDemo();
}
private async _setupDemo() {
const hass = provideHass(this);
await hass.updateTranslations(null, "en");
protected firstUpdated(changedProperties: PropertyValues) {
super.firstUpdated(changedProperties);
const hass = provideHass(this._demoRoot);
hass.updateTranslations(null, "en");
hass.addEntities(ENTITIES);
}
}

View File

@@ -1,9 +1,10 @@
import "@material/mwc-button";
import { html, LitElement, TemplateResult } from "lit-element";
import { customElement, html, LitElement, TemplateResult } from "lit-element";
import "../../../src/components/ha-card";
import { ActionHandlerEvent } from "../../../src/data/lovelace";
import { actionHandler } from "../../../src/panels/lovelace/common/directives/action-handler-directive";
@customElement("demo-util-long-press")
export class DemoUtilLongPress extends LitElement {
protected render(): TemplateResult {
return html`
@@ -20,7 +21,7 @@ export class DemoUtilLongPress extends LitElement {
<textarea></textarea>
<div>(try pressing and scrolling too!)</div>
<div>Try pressing and scrolling too!</div>
</ha-card>
`
)}
@@ -62,5 +63,3 @@ export class DemoUtilLongPress extends LitElement {
`;
}
}
customElements.define("demo-util-long-press", DemoUtilLongPress);

View File

@@ -14,8 +14,6 @@ import "../../src/styles/polymer-ha-style";
// eslint-disable-next-line import/extensions
import { DEMOS } from "../build/import-demos";
const fixPath = (path) => path.substr(2, path.length - 5);
class HaGallery extends PolymerElement {
static get template() {
return html`

View File

@@ -69,7 +69,7 @@ const STAGE_ICON = {
const PERMIS_DESC = {
stage: {
title: "Add-on Stage",
description: `Add-ons can have one of three stages:\n\n<ha-svg-icon .path='${STAGE_ICON.stable}'></ha-svg-icon> **Stable**: These are add-ons ready to be used in production.\n\n<ha-svg-icon .path='${STAGE_ICON.experimental}'></ha-svg-icon> **Experimental**: These may contain bugs, and may be unfinished.\n\n<ha-svg-icon .path='${STAGE_ICON.deprecated}'></ha-svg-icon> **Deprecated**: These add-ons will no longer receive any updates.`,
description: `Add-ons can have one of three stages:\n\n<ha-svg-icon path="${STAGE_ICON.stable}"></ha-svg-icon> **Stable**: These are add-ons ready to be used in production.\n\n<ha-svg-icon path="${STAGE_ICON.experimental}"></ha-svg-icon> **Experimental**: These may contain bugs, and may be unfinished.\n\n<ha-svg-icon path="${STAGE_ICON.deprecated}"></ha-svg-icon> **Deprecated**: These add-ons will no longer receive any updates.`,
},
rating: {
title: "Add-on Security Rating",

View File

@@ -178,7 +178,7 @@ class HassioSupervisorInfo extends LitElement {
</div>`}
${!this.supervisor.supervisor.healthy
? html`<div class="error">
Your installtion is running in an unhealthy state.
Your installation is running in an unhealthy state.
<button
class="link"
title="Learn more about why your system is marked as unhealthy"

View File

@@ -29,22 +29,22 @@
"@fullcalendar/daygrid": "5.1.0",
"@fullcalendar/interaction": "5.1.0",
"@fullcalendar/list": "5.1.0",
"@material/chips": "=8.0.0-canary.774dcfc8e.0",
"@material/mwc-button": "^0.19.0",
"@material/mwc-checkbox": "^0.19.0",
"@material/mwc-circular-progress": "^0.19.0",
"@material/mwc-dialog": "^0.19.0",
"@material/mwc-fab": "^0.19.0",
"@material/mwc-formfield": "^0.19.0",
"@material/mwc-icon-button": "^0.19.0",
"@material/mwc-list": "^0.19.0",
"@material/mwc-menu": "^0.19.0",
"@material/mwc-radio": "^0.19.0",
"@material/mwc-ripple": "^0.19.0",
"@material/mwc-switch": "^0.19.0",
"@material/mwc-tab": "^0.19.0",
"@material/mwc-tab-bar": "^0.19.0",
"@material/top-app-bar": "=8.0.0-canary.774dcfc8e.0",
"@material/chips": "=9.0.0-canary.1c156d69d.0",
"@material/mwc-button": "^0.20.0",
"@material/mwc-checkbox": "^0.20.0",
"@material/mwc-circular-progress": "^0.20.0",
"@material/mwc-dialog": "^0.20.0",
"@material/mwc-fab": "^0.20.0",
"@material/mwc-formfield": "^0.20.0",
"@material/mwc-icon-button": "^0.20.0",
"@material/mwc-list": "^0.20.0",
"@material/mwc-menu": "^0.20.0",
"@material/mwc-radio": "^0.20.0",
"@material/mwc-ripple": "^0.20.0",
"@material/mwc-switch": "^0.20.0",
"@material/mwc-tab": "^0.20.0",
"@material/mwc-tab-bar": "^0.20.0",
"@material/top-app-bar": "=9.0.0-canary.1c156d69d.0",
"@mdi/js": "5.6.55",
"@mdi/svg": "5.6.55",
"@polymer/app-layout": "^3.0.2",

View File

@@ -2,7 +2,7 @@ from setuptools import setup, find_packages
setup(
name="home-assistant-frontend",
version="20201212.0",
version="20201229.0",
description="The Home Assistant frontend",
url="https://github.com/home-assistant/home-assistant-polymer",
author="The Home Assistant Authors",

View File

@@ -1,12 +1,17 @@
export const copyToClipboard = (str) => {
export const copyToClipboard = async (str) => {
if (navigator.clipboard) {
navigator.clipboard.writeText(str);
} else {
const el = document.createElement("textarea");
el.value = str;
document.body.appendChild(el);
el.select();
document.execCommand("copy");
document.body.removeChild(el);
try {
await navigator.clipboard.writeText(str);
return;
} catch {
// just continue with the fallback coding below
}
}
const el = document.createElement("textarea");
el.value = str;
document.body.appendChild(el);
el.select();
document.execCommand("copy");
document.body.removeChild(el);
};

View File

@@ -99,7 +99,7 @@ export class HaDataTable extends LitElement {
@property({ type: Boolean }) public hasFab = false;
/**
* Add an extra rows at the bottom of the datatabel
* Add an extra row at the bottom of the data table
* @type {TemplateResult}
*/
@property({ attribute: false }) public appendRow?;

View File

@@ -58,6 +58,14 @@ const sortData = (
valB = valB.toUpperCase();
}
// Ensure "undefined" is always sorted to the bottom
if (valA === undefined && valB !== undefined) {
return 1;
}
if (valB === undefined && valA !== undefined) {
return -1;
}
if (valA < valB) {
return sort * -1;
}

View File

@@ -63,7 +63,7 @@ class HaAttributes extends LitElement {
justify-content: space-between;
}
.data-entry .value {
max-width: 50%;
max-width: 60%;
overflow-wrap: break-word;
text-align: right;
}
@@ -77,6 +77,9 @@ class HaAttributes extends LitElement {
pre {
font-family: inherit;
font-size: inherit;
margin: 0px;
overflow-wrap: break-word;
white-space: pre-line;
}
`;
}

View File

@@ -10,7 +10,6 @@ class HaLabeledSlider extends PolymerElement {
<style>
:host {
display: block;
border-radius: 4px;
}
.title {
@@ -30,6 +29,7 @@ class HaLabeledSlider extends PolymerElement {
ha-slider {
flex-grow: 1;
background-image: var(--ha-slider-background);
border-radius: 4px;
}
</style>

View File

@@ -124,13 +124,17 @@ export const getLogbookMessage = (
switch (domain) {
case "device_tracker":
case "person":
return state === "not_home"
? hass.localize(`${LOGBOOK_LOCALIZE_PATH}.was_away`)
: hass.localize(
`${LOGBOOK_LOCALIZE_PATH}.was_at_state`,
"state",
state
);
if (state === "not_home") {
return hass.localize(`${LOGBOOK_LOCALIZE_PATH}.was_away`);
}
if (state === "home") {
return hass.localize(`${LOGBOOK_LOCALIZE_PATH}.was_at_home`);
}
return hass.localize(
`${LOGBOOK_LOCALIZE_PATH}.was_at_state`,
"state",
state
);
case "sun":
return state === "above_horizon"

View File

@@ -148,6 +148,11 @@ export interface CustomActionConfig extends BaseActionConfig {
action: "fire-dom-event";
}
export interface MultipleActionConfig extends BaseActionConfig {
action: "multiple";
actions: ActionConfig[];
}
export interface BaseActionConfig {
confirmation?: ConfirmationRestrictionConfig;
}
@@ -168,7 +173,8 @@ export type ActionConfig =
| UrlActionConfig
| MoreInfoActionConfig
| NoActionConfig
| CustomActionConfig;
| CustomActionConfig
| MultipleActionConfig;
type LovelaceUpdatedEvent = HassEventBase & {
event_type: "lovelace_updated";

View File

@@ -38,3 +38,12 @@ export const addItem = (
type: "shopping_list/items/add",
name,
});
export const reorderItems = (
hass: HomeAssistant,
itemIds: [string]
): Promise<ShoppingListItem> =>
hass.callWS({
type: "shopping_list/items/reorder",
item_ids: itemIds,
});

View File

@@ -10,6 +10,7 @@ import {
TemplateResult,
} from "lit-element";
import { HomeAssistant } from "../../../types";
import { UNAVAILABLE_STATES } from "../../../data/entity";
@customElement("more-info-counter")
class MoreInfoCounter extends LitElement {
@@ -22,21 +23,29 @@ class MoreInfoCounter extends LitElement {
return html``;
}
const disabled = UNAVAILABLE_STATES.includes(this.stateObj!.state);
return html`
<div class="actions">
<mwc-button
.action="${"increment"}"
@click="${this._handleActionClick}"
@click=${this._handleActionClick}
.disabled=${disabled}
>
${this.hass!.localize("ui.card.counter.actions.increment")}
</mwc-button>
<mwc-button
.action="${"decrement"}"
@click="${this._handleActionClick}"
@click=${this._handleActionClick}
.disabled=${disabled}
>
${this.hass!.localize("ui.card.counter.actions.decrement")}
</mwc-button>
<mwc-button .action="${"reset"}" @click="${this._handleActionClick}">
<mwc-button
.action="${"reset"}"
@click=${this._handleActionClick}
.disabled=${disabled}
>
${this.hass!.localize("ui.card.counter.actions.reset")}
</mwc-button>
</div>
@@ -53,8 +62,7 @@ class MoreInfoCounter extends LitElement {
static get styles(): CSSResult {
return css`
.actions {
margin: 0;
padding-top: 20px;
margin: 8px 0;
display: flex;
flex-wrap: wrap;
justify-content: center;

View File

@@ -75,7 +75,7 @@ class MoreInfoPerson extends LitElement {
justify-content: space-between;
}
.actions {
margin: 36px 0 8px 0;
margin: 8px 0;
text-align: right;
}
ha-map {

View File

@@ -80,6 +80,7 @@ class MoreInfoSun extends LitElement {
}
ha-relative-time {
display: inline-block;
white-space: nowrap;
}
ha-relative-time::first-letter {
text-transform: lowercase;

View File

@@ -76,8 +76,7 @@ class MoreInfoTimer extends LitElement {
static get styles(): CSSResult {
return css`
.actions {
margin: 0;
padding-top: 20px;
margin: 8px 0;
display: flex;
flex-wrap: wrap;
justify-content: center;

View File

@@ -61,7 +61,7 @@ export class HaTabsSubpageDataTable extends LitElement {
@property({ type: Boolean }) public hasFab = false;
/**
* Add an extra rows at the bottom of the datatabel
* Add an extra row at the bottom of the data table
* @type {TemplateResult}
*/
@property({ attribute: false }) public appendRow?;

View File

@@ -15,12 +15,14 @@ import {
LitElement,
property,
PropertyValues,
query,
} from "lit-element";
import { dynamicElement } from "../../../../common/dom/dynamic-element-directive";
import { fireEvent } from "../../../../common/dom/fire_event";
import "../../../../components/ha-button-menu";
import "../../../../components/ha-card";
import "../../../../components/ha-svg-icon";
import type { HaYamlEditor } from "../../../../components/ha-yaml-editor";
import type { Action } from "../../../../data/script";
import { showConfirmationDialog } from "../../../../dialogs/generic/show-dialog-box";
import { haStyle } from "../../../../resources/styles";
@@ -103,6 +105,8 @@ export default class HaAutomationActionRow extends LitElement {
@internalProperty() private _yamlMode = false;
@query("ha-yaml-editor") private _yamlEditor?: HaYamlEditor;
protected updated(changedProperties: PropertyValues) {
if (!changedProperties.has("action")) {
return;
@@ -111,6 +115,10 @@ export default class HaAutomationActionRow extends LitElement {
if (!this._uiModeAvailable && !this._yamlMode) {
this._yamlMode = true;
}
if (this._yamlMode && this._yamlEditor) {
this._yamlEditor.setValue(this.action);
}
}
protected render() {
@@ -187,7 +195,7 @@ export default class HaAutomationActionRow extends LitElement {
<ul>
${this._warnings.map((warning) => html`<li>${warning}</li>`)}
</ul>
You can still edit your config in yaml.
You can still edit your config in YAML.
</div>`
: ""}
${yamlMode

View File

@@ -19,12 +19,12 @@ import type { HaYamlEditor } from "../../../../../components/ha-yaml-editor";
import { ServiceAction } from "../../../../../data/script";
import type { PolymerChangedEvent } from "../../../../../polymer-types";
import type { HomeAssistant } from "../../../../../types";
import { EntityId } from "../../../../lovelace/common/structs/is-entity-id";
import { EntityIdOrAll } from "../../../../lovelace/common/structs/is-entity-id";
import { ActionElement, handleChangeEvent } from "../ha-automation-action-row";
const actionStruct = object({
service: optional(string()),
entity_id: optional(EntityId),
entity_id: optional(EntityIdOrAll),
data: optional(any()),
});

View File

@@ -165,7 +165,7 @@ export class HaBlueprintAutomationEditor extends LitElement {
.selector=${value.selector}
.key=${key}
.value=${(this.config.use_blueprint.input &&
this.config.use_blueprint.input[key]) ||
this.config.use_blueprint.input[key]) ??
value?.default}
@value-changed=${this._inputChanged}
></ha-selector>`

View File

@@ -395,9 +395,12 @@ export class HaAutomationEditor extends KeyboardShortcutMixin(LitElement) {
return cleanConfig;
}
private async _copyYaml() {
private async _copyYaml(): Promise<void> {
if (this._editor?.yaml) {
copyToClipboard(this._editor.yaml);
await copyToClipboard(this._editor.yaml);
showToast(this, {
message: this.hass.localize("ui.common.copied_clipboard"),
});
}
}

View File

@@ -92,7 +92,7 @@ class HaConfigCustomize extends LocalizeMixin(PolymerElement) {
}
_computeTabs() {
return configSections.general;
return configSections.advanced;
}
computeEntities(hass) {

View File

@@ -189,9 +189,7 @@ export class HaConfigDeviceDashboard extends LitElement {
),
model: device.model || "<unknown>",
manufacturer: device.manufacturer || "<unknown>",
area: device.area_id
? areaLookup[device.area_id].name
: this.hass.localize("ui.panel.config.devices.data_table.no_area"),
area: device.area_id ? areaLookup[device.area_id].name : undefined,
integration: device.config_entries.length
? device.config_entries
.filter((entId) => entId in entryLookup)

View File

@@ -197,7 +197,7 @@ class SystemHealthCard extends LitElement {
});
}
private _copyInfo(ev: CustomEvent<ActionDetail>): void {
private async _copyInfo(ev: CustomEvent<ActionDetail>): Promise<void> {
const github = ev.detail.index === 1;
let haContent: string | undefined;
const domainParts: string[] = [];
@@ -250,13 +250,15 @@ class SystemHealthCard extends LitElement {
}
}
copyToClipboard(
await copyToClipboard(
`${github ? "## " : ""}System Health\n${haContent}\n\n${domainParts.join(
"\n\n"
)}`
);
showToast(this, { message: this.hass.localize("ui.common.copied") });
showToast(this, {
message: this.hass.localize("ui.common.copied_clipboard"),
});
}
static get styles(): CSSResult {

View File

@@ -141,11 +141,15 @@ class OZWNodeDashboard extends LitElement {
${this._metadata.metadata.ResetHelp}
</div>
</ha-card>
<ha-card class="content" header="WakeUp">
<div class="card-content">
${this._metadata.metadata.WakeupHelp}
</div>
</ha-card>
${this._metadata.metadata.WakeupHelp
? html`
<ha-card class="content" header="WakeUp">
<div class="card-content">
${this._metadata.metadata.WakeupHelp}
</div>
</ha-card>
`
: ``}
`
: ``}
`
@@ -199,6 +203,10 @@ class OZWNodeDashboard extends LitElement {
margin-top: 24px;
}
.content:last-child {
margin-bottom: 24px;
}
.sectionHeader {
position: relative;
padding-right: 40px;

View File

@@ -49,19 +49,17 @@ class ZHADevicePairingStatusCard extends LitElement {
class="discovered ${classMap({
initialized: this.device.pairing_status === INITIALIZED,
})}"
><div
class="header"
>
<h1>
><div class="header">
<h4>
${this.hass!.localize(
`ui.panel.config.zha.device_pairing_card.${this.device.pairing_status}`
)}
</h1>
<h4>
</h4>
<h1>
${this.hass!.localize(
`ui.panel.config.zha.device_pairing_card.${this.device.pairing_status}_status_text`
)}
</h4>
</h1>
</div>
<div class="card-content">
${[INTERVIEW_COMPLETE, CONFIGURED].includes(

View File

@@ -15,6 +15,12 @@ import { fetchDevices, ZHADevice } from "../../../../../data/zha";
import "../../../../../layouts/hass-subpage";
import type { HomeAssistant } from "../../../../../types";
import { Network, Edge, Node, EdgeOptions } from "vis-network";
import "../../../../../common/search/search-input";
import "../../../../../components/ha-button-menu";
import "../../../../../components/device/ha-device-picker";
import "../../../../../components/ha-svg-icon";
import { formatAsPaddedHex } from "./functions";
import { PolymerChangedEvent } from "../../../../../polymer-types";
@customElement("zha-network-visualization-page")
export class ZHANetworkVisualizationPage extends LitElement {
@@ -28,9 +34,21 @@ export class ZHANetworkVisualizationPage extends LitElement {
@internalProperty()
private _devices: Map<string, ZHADevice> = new Map();
@internalProperty()
private _devicesByDeviceId: Map<string, ZHADevice> = new Map();
@internalProperty()
private _nodes: Node[] = [];
@internalProperty()
private _network?: Network;
@internalProperty()
private _filter?: string;
@internalProperty()
private _zoomedDeviceId?: string;
protected firstUpdated(changedProperties: PropertyValues): void {
super.firstUpdated(changedProperties);
if (this.hass) {
@@ -91,6 +109,27 @@ export class ZHANetworkVisualizationPage extends LitElement {
"ui.panel.config.zha.visualization.header"
)}
>
<div class="table-header">
<search-input
no-label-float
no-underline
@value-changed=${this._handleSearchChange}
.filter=${this._filter}
.label=${this.hass.localize(
"ui.panel.config.zha.visualization.highlight_label"
)}
>
</search-input>
<ha-device-picker
.hass=${this.hass}
.value=${this._zoomedDeviceId}
.label=${this.hass.localize(
"ui.panel.config.zha.visualization.zoom_label"
)}
.includeDomains="['zha']"
@value-changed=${this._zoomToDevice}
></ha-device-picker>
</div>
<div id="visualization"></div>
</hass-subpage>
`;
@@ -101,15 +140,18 @@ export class ZHANetworkVisualizationPage extends LitElement {
this._devices = new Map(
devices.map((device: ZHADevice) => [device.ieee, device])
);
this._devicesByDeviceId = new Map(
devices.map((device: ZHADevice) => [device.device_reg_id, device])
);
this._updateDevices(devices);
}
private _updateDevices(devices: ZHADevice[]) {
const nodes: Node[] = [];
this._nodes = [];
const edges: Edge[] = [];
devices.forEach((device) => {
nodes.push({
this._nodes.push({
id: device.ieee,
label: this._buildLabel(device),
shape: this._getShape(device),
@@ -137,7 +179,7 @@ export class ZHANetworkVisualizationPage extends LitElement {
}
});
this._network?.setData({ nodes: nodes, edges: edges });
this._network?.setData({ nodes: this._nodes, edges: edges });
}
private _getLQI(lqi: number): EdgeOptions["color"] {
@@ -181,7 +223,7 @@ export class ZHANetworkVisualizationPage extends LitElement {
label += `<b>IEEE: </b>${device.ieee}`;
label += `\n<b>Device Type: </b>${device.device_type.replace("_", " ")}`;
if (device.nwk != null) {
label += `\n<b>NWK: </b>${device.nwk}`;
label += `\n<b>NWK: </b>${formatAsPaddedHex(device.nwk)}`;
}
if (device.manufacturer != null && device.model != null) {
label += `\n<b>Device: </b>${device.manufacturer} ${device.model}`;
@@ -194,6 +236,56 @@ export class ZHANetworkVisualizationPage extends LitElement {
return label;
}
private _handleSearchChange(ev: CustomEvent) {
this._filter = ev.detail.value;
const filterText = this._filter!.toLowerCase();
if (!this._network) {
return;
}
if (this._filter) {
const filteredNodeIds: (string | number)[] = [];
this._nodes.forEach((node) => {
if (node.label && node.label.toLowerCase().includes(filterText)) {
filteredNodeIds.push(node.id!);
}
});
this._zoomedDeviceId = "";
this._zoomOut();
this._network.selectNodes(filteredNodeIds, true);
} else {
this._network.unselectAll();
}
}
private _zoomToDevice(event: PolymerChangedEvent<string>) {
event.stopPropagation();
this._zoomedDeviceId = event.detail.value;
if (!this._network) {
return;
}
this._filter = "";
if (!this._zoomedDeviceId) {
this._zoomOut();
} else {
const device: ZHADevice | undefined = this._devicesByDeviceId.get(
this._zoomedDeviceId
);
if (device) {
this._network.fit({
nodes: [device.ieee],
animation: { duration: 500, easingFunction: "easeInQuad" },
});
}
}
}
private _zoomOut() {
this._network!.fit({
nodes: [],
animation: { duration: 500, easingFunction: "easeOutQuad" },
});
}
static get styles(): CSSResult[] {
return [
css`
@@ -208,6 +300,30 @@ export class ZHANetworkVisualizationPage extends LitElement {
line-height: var(--paper-font-display1_-_line-height);
opacity: var(--dark-primary-opacity);
}
.table-header {
border-bottom: 1px solid --divider-color;
padding: 0 16px;
display: flex;
align-items: center;
height: var(--header-height);
}
.search-toolbar {
display: flex;
align-items: center;
color: var(--secondary-text-color);
padding: 0 16px;
}
search-input {
position: relative;
top: 2px;
flex: 1;
}
search-input.header {
left: -8px;
}
ha-device-picker {
flex: 1;
}
`,
];
}

View File

@@ -2,7 +2,6 @@ import "../../../components/ha-header-bar";
import "@material/mwc-icon-button/mwc-icon-button";
import { mdiContentCopy, mdiClose } from "@mdi/js";
import "@polymer/paper-tooltip/paper-tooltip";
import type { PaperTooltipElement } from "@polymer/paper-tooltip/paper-tooltip";
import {
css,
CSSResult,
@@ -10,10 +9,10 @@ import {
internalProperty,
LitElement,
property,
query,
TemplateResult,
} from "lit-element";
import { fireEvent } from "../../../common/dom/fire_event";
import { copyToClipboard } from "../../../common/util/copy-clipboard";
import "../../../components/ha-dialog";
import "../../../components/ha-svg-icon";
import {
@@ -27,6 +26,7 @@ import { haStyleDialog } from "../../../resources/styles";
import type { HomeAssistant } from "../../../types";
import type { SystemLogDetailDialogParams } from "./show-dialog-system-log-detail";
import { formatSystemLogTime } from "./util";
import { showToast } from "../../../util/toast";
class DialogSystemLogDetail extends LitElement {
@property({ attribute: false }) public hass!: HomeAssistant;
@@ -35,8 +35,6 @@ class DialogSystemLogDetail extends LitElement {
@internalProperty() private _manifest?: IntegrationManifest;
@query("paper-tooltip") private _toolTip?: PaperTooltipElement;
public async showDialog(params: SystemLogDetailDialogParams): Promise<void> {
this._params = params;
this._manifest = undefined;
@@ -83,15 +81,6 @@ class DialogSystemLogDetail extends LitElement {
<mwc-icon-button id="copy" @click=${this._copyLog} slot="actionItems">
<ha-svg-icon .path=${mdiContentCopy}></ha-svg-icon>
</mwc-icon-button>
<paper-tooltip
slot="actionItems"
manual-mode
for="copy"
position="left"
animation-delay="0"
offset="4"
>${this.hass.localize("ui.common.copied")}</paper-tooltip
>
</ha-header-bar>
<div class="contents">
<p>
@@ -162,23 +151,15 @@ class DialogSystemLogDetail extends LitElement {
}
}
private _copyLog(): void {
private async _copyLog(): Promise<void> {
const copyElement = this.shadowRoot?.querySelector(
".contents"
) as HTMLElement;
const selection = window.getSelection()!;
const range = document.createRange();
range.selectNodeContents(copyElement);
selection.removeAllRanges();
selection.addRange(range);
document.execCommand("copy");
window.getSelection()!.removeAllRanges();
this._toolTip!.show();
setTimeout(() => this._toolTip?.hide(), 3000);
await copyToClipboard(copyElement.innerText);
showToast(this, {
message: this.hass.localize("ui.common.copied_clipboard"),
});
}
static get styles(): CSSResult[] {

View File

@@ -544,9 +544,12 @@ export class HaScriptEditor extends KeyboardShortcutMixin(LitElement) {
return this._config;
}
private async _copyYaml() {
private async _copyYaml(): Promise<void> {
if (this._editor?.yaml) {
copyToClipboard(this._editor.yaml);
await copyToClipboard(this._editor.yaml);
showToast(this, {
message: this.hass.localize("ui.common.copied_clipboard"),
});
}
}

View File

@@ -119,27 +119,21 @@ class HaPanelHistory extends LitElement {
todayEnd.setDate(todayEnd.getDate() + 1);
todayEnd.setMilliseconds(todayEnd.getMilliseconds() - 1);
const todayCopy = new Date(today);
const yesterday = new Date(todayCopy.setDate(today.getDate() - 1));
const yesterdayEnd = new Date(yesterday);
yesterdayEnd.setDate(yesterdayEnd.getDate() + 1);
const yesterday = new Date(today);
yesterday.setDate(today.getDate() - 1);
const yesterdayEnd = new Date(today);
yesterdayEnd.setMilliseconds(yesterdayEnd.getMilliseconds() - 1);
const thisWeekStart = new Date(
todayCopy.setDate(today.getDate() - today.getDay())
);
const thisWeekEnd = new Date(
todayCopy.setDate(thisWeekStart.getDate() + 7)
);
const thisWeekStart = new Date(today);
thisWeekStart.setDate(today.getDate() - today.getDay());
const thisWeekEnd = new Date(thisWeekStart);
thisWeekEnd.setDate(thisWeekStart.getDate() + 7);
thisWeekEnd.setMilliseconds(thisWeekEnd.getMilliseconds() - 1);
const lastWeekStart = new Date(
todayCopy.setDate(today.getDate() - today.getDay() - 7)
);
const lastWeekEnd = new Date(
todayCopy.setDate(lastWeekStart.getDate() + 7)
);
const lastWeekStart = new Date(today);
lastWeekStart.setDate(today.getDate() - today.getDay() - 7);
const lastWeekEnd = new Date(lastWeekStart);
lastWeekEnd.setDate(lastWeekStart.getDate() + 7);
lastWeekEnd.setMilliseconds(lastWeekEnd.getMilliseconds() - 1);
this._ranges = {

View File

@@ -24,7 +24,7 @@ class HaPanelIframe extends PolymerElement {
<iframe
src="[[panel.config.url]]"
sandbox="allow-forms allow-popups allow-pointer-lock allow-same-origin allow-scripts"
sandbox="allow-forms allow-popups allow-pointer-lock allow-same-origin allow-scripts allow-modals"
allowfullscreen="true"
webkitallowfullscreen="true"
mozallowfullscreen="true"

View File

@@ -147,27 +147,21 @@ export class HaPanelLogbook extends LitElement {
todayEnd.setDate(todayEnd.getDate() + 1);
todayEnd.setMilliseconds(todayEnd.getMilliseconds() - 1);
const todayCopy = new Date(today);
const yesterday = new Date(todayCopy.setDate(today.getDate() - 1));
const yesterdayEnd = new Date(yesterday);
yesterdayEnd.setDate(yesterdayEnd.getDate() + 1);
const yesterday = new Date(today);
yesterday.setDate(today.getDate() - 1);
const yesterdayEnd = new Date(today);
yesterdayEnd.setMilliseconds(yesterdayEnd.getMilliseconds() - 1);
const thisWeekStart = new Date(
todayCopy.setDate(today.getDate() - today.getDay())
);
const thisWeekEnd = new Date(
todayCopy.setDate(thisWeekStart.getDate() + 7)
);
const thisWeekStart = new Date(today);
thisWeekStart.setDate(today.getDate() - today.getDay());
const thisWeekEnd = new Date(thisWeekStart);
thisWeekEnd.setDate(thisWeekStart.getDate() + 7);
thisWeekEnd.setMilliseconds(thisWeekEnd.getMilliseconds() - 1);
const lastWeekStart = new Date(
todayCopy.setDate(today.getDate() - today.getDay() - 7)
);
const lastWeekEnd = new Date(
todayCopy.setDate(lastWeekStart.getDate() + 7)
);
const lastWeekStart = new Date(today);
lastWeekStart.setDate(today.getDate() - today.getDay() - 7);
const lastWeekEnd = new Date(lastWeekStart);
lastWeekEnd.setDate(lastWeekStart.getDate() + 7);
lastWeekEnd.setMilliseconds(lastWeekEnd.getMilliseconds() - 1);
this._ranges = {

View File

@@ -122,12 +122,14 @@ class HuiGaugeCard extends LitElement implements LovelaceCard {
`;
}
// Use `stateObj.state` as value to keep formatting (e.g trailing zeros)
// for consistent value display across gauge, entity, entity-row, etc.
return html`
<ha-card @click=${this._handleClick} tabindex="0">
<ha-gauge
.min=${this._config.min!}
.max=${this._config.max!}
.value=${state}
.value=${stateObj.state}
.language=${this.hass!.language}
.label=${this._config!.unit ||
this.hass?.states[this._config!.entity].attributes

View File

@@ -11,9 +11,12 @@ import {
property,
PropertyValues,
TemplateResult,
query,
} from "lit-element";
import { classMap } from "lit-html/directives/class-map";
import { repeat } from "lit-html/directives/repeat";
import { guard } from "lit-html/directives/guard";
import { mdiDrag, mdiSort, mdiPlus, mdiNotificationClearAll } from "@mdi/js";
import { applyThemesOnElement } from "../../../common/dom/apply_themes_on_element";
import "../../../components/ha-card";
import "../../../components/ha-icon";
@@ -23,12 +26,15 @@ import {
fetchItems,
ShoppingListItem,
updateItem,
reorderItems,
} from "../../../data/shopping-list";
import { SubscribeMixin } from "../../../mixins/subscribe-mixin";
import { HomeAssistant } from "../../../types";
import { LovelaceCard, LovelaceCardEditor } from "../types";
import { SensorCardConfig, ShoppingListCardConfig } from "./types";
let Sortable;
@customElement("hui-shopping-list-card")
class HuiShoppingListCard extends SubscribeMixin(LitElement)
implements LovelaceCard {
@@ -49,6 +55,14 @@ class HuiShoppingListCard extends SubscribeMixin(LitElement)
@internalProperty() private _checkedItems?: ShoppingListItem[];
@internalProperty() private _reordering = false;
@internalProperty() private _renderEmptySortable = false;
private _sortable?;
@query("#sortable") private _sortableEl?: HTMLElement;
public getCardSize(): number {
return (this._config ? (this._config.title ? 2 : 0) : 0) + 3;
}
@@ -101,15 +115,15 @@ class HuiShoppingListCard extends SubscribeMixin(LitElement)
})}
>
<div class="addRow">
<ha-icon
<ha-svg-icon
class="addButton"
icon="hass:plus"
.path=${mdiPlus}
.title=${this.hass!.localize(
"ui.panel.lovelace.cards.shopping-list.add_item"
)}
@click=${this._addItem}
>
</ha-icon>
</ha-svg-icon>
<paper-input
no-label-float
class="addBox"
@@ -118,28 +132,27 @@ class HuiShoppingListCard extends SubscribeMixin(LitElement)
)}
@keydown=${this._addKeyPress}
></paper-input>
<ha-svg-icon
class="reorderButton"
.path=${mdiSort}
.title=${this.hass!.localize(
"ui.panel.lovelace.cards.shopping-list.reorder_items"
)}
@click=${this._toggleReorder}
>
</ha-svg-icon>
</div>
${repeat(
this._uncheckedItems!,
(item) => item.id,
(item) =>
html`
<div class="editRow">
<paper-checkbox
tabindex="0"
?checked=${item.complete}
.itemId=${item.id}
@click=${this._completeItem}
></paper-checkbox>
<paper-input
no-label-float
.value=${item.name}
.itemId=${item.id}
@change=${this._saveEdit}
></paper-input>
${this._reordering
? html`
<div id="sortable">
${guard([this._uncheckedItems, this._renderEmptySortable], () =>
this._renderEmptySortable
? ""
: this._renderItems(this._uncheckedItems!)
)}
</div>
`
)}
: this._renderItems(this._uncheckedItems!)}
${this._checkedItems!.length > 0
? html`
<div class="divider"></div>
@@ -149,16 +162,16 @@ class HuiShoppingListCard extends SubscribeMixin(LitElement)
"ui.panel.lovelace.cards.shopping-list.checked_items"
)}
</span>
<ha-icon
<ha-svg-icon
class="clearall"
tabindex="0"
icon="hass:notification-clear-all"
.path=${mdiNotificationClearAll}
.title=${this.hass!.localize(
"ui.panel.lovelace.cards.shopping-list.clear_items"
)}
@click=${this._clearItems}
>
</ha-icon>
</ha-svg-icon>
</div>
${repeat(
this._checkedItems!,
@@ -187,6 +200,44 @@ class HuiShoppingListCard extends SubscribeMixin(LitElement)
`;
}
private _renderItems(items: ShoppingListItem[]) {
return html`
${repeat(
items,
(item) => item.id,
(item) =>
html`
<div class="editRow" item-id=${item.id}>
<paper-checkbox
tabindex="0"
?checked=${item.complete}
.itemId=${item.id}
@click=${this._completeItem}
></paper-checkbox>
<paper-input
no-label-float
.value=${item.name}
.itemId=${item.id}
@change=${this._saveEdit}
></paper-input>
${this._reordering
? html`
<ha-svg-icon
.title=${this.hass!.localize(
"ui.panel.lovelace.cards.shopping-list.drag_and_drop"
)}
class="reorderButton"
.path=${mdiDrag}
>
</ha-svg-icon>
`
: ""}
</div>
`
)}
`;
}
private async _fetchData(): Promise<void> {
if (!this.hass) {
return;
@@ -248,6 +299,54 @@ class HuiShoppingListCard extends SubscribeMixin(LitElement)
}
}
private async _toggleReorder() {
if (!Sortable) {
const sortableImport = await import(
"sortablejs/modular/sortable.core.esm"
);
Sortable = sortableImport.Sortable;
}
this._reordering = !this._reordering;
await this.updateComplete;
if (this._reordering) {
this._createSortable();
} else {
this._sortable?.destroy();
this._sortable = undefined;
}
}
private _createSortable() {
const sortableEl = this._sortableEl;
this._sortable = new Sortable(sortableEl, {
animation: 150,
fallbackClass: "sortable-fallback",
dataIdAttr: "item-id",
handle: "ha-svg-icon",
onEnd: async (evt) => {
// Since this is `onEnd` event, it's possible that
// an item wa dragged away and was put back to its original position.
if (evt.oldIndex !== evt.newIndex) {
reorderItems(this.hass!, this._sortable.toArray()).catch(() =>
this._fetchData()
);
// Move the shopping list item in memory.
this._uncheckedItems!.splice(
evt.newIndex,
0,
this._uncheckedItems!.splice(evt.oldIndex, 1)[0]
);
}
this._renderEmptySortable = true;
await this.updateComplete;
while (sortableEl?.lastElementChild) {
sortableEl.removeChild(sortableEl.lastElementChild);
}
this._renderEmptySortable = false;
},
});
}
static get styles(): CSSResult {
return css`
ha-card {
@@ -278,6 +377,11 @@ class HuiShoppingListCard extends SubscribeMixin(LitElement)
cursor: pointer;
}
.reorderButton {
padding-left: 16px;
cursor: pointer;
}
paper-checkbox {
padding-left: 4px;
padding-right: 20px;

View File

@@ -4,6 +4,7 @@ import { forwardHaptic } from "../../../data/haptics";
import { ActionConfig } from "../../../data/lovelace";
import { HomeAssistant } from "../../../types";
import { toggleEntity } from "./entity/toggle-entity";
import { showConfirmationDialog } from "../../../dialogs/generic/show-dialog-box";
declare global {
interface HASSDomEvents {
@@ -11,7 +12,7 @@ declare global {
}
}
export const handleAction = (
export const handleAction = async (
node: HTMLElement,
hass: HomeAssistant,
config: {
@@ -22,7 +23,7 @@ export const handleAction = (
double_tap_action?: ActionConfig;
},
action: string
): void => {
): Promise<void> => {
let actionConfig: ActionConfig | undefined;
if (action === "double_tap" && config.double_tap_action) {
@@ -39,64 +40,78 @@ export const handleAction = (
};
}
if (
actionConfig.confirmation &&
(!actionConfig.confirmation.exemptions ||
!actionConfig.confirmation.exemptions.some(
(e) => e.user === hass!.user!.id
))
) {
forwardHaptic("warning");
const actionConfigs =
actionConfig.action === "multiple"
? Array.isArray(actionConfig.actions)
? actionConfig.actions
: [actionConfig.actions]
: [actionConfig];
for await (actionConfig of actionConfigs) {
if (
!confirm(
actionConfig.confirmation.text ||
`Are you sure you want to ${actionConfig.action}?`
)
actionConfig.confirmation &&
(!actionConfig.confirmation.exemptions ||
!actionConfig.confirmation.exemptions.some(
(e) => e.user === hass!.user!.id
))
) {
return;
}
}
forwardHaptic("warning");
switch (actionConfig.action) {
case "more-info": {
if (config.entity || config.camera_image) {
fireEvent(node, "hass-more-info", {
entityId: config.entity ? config.entity : config.camera_image!,
});
}
break;
}
case "navigate":
if (actionConfig.navigation_path) {
navigate(node, actionConfig.navigation_path);
}
break;
case "url": {
if (actionConfig.url_path) {
window.open(actionConfig.url_path);
}
break;
}
case "toggle": {
if (config.entity) {
toggleEntity(hass, config.entity!);
forwardHaptic("light");
}
break;
}
case "call-service": {
if (!actionConfig.service) {
forwardHaptic("failure");
if (
!(await showConfirmationDialog(node, {
text:
actionConfig.confirmation.text ||
hass.localize(
"ui.panel.lovelace.cards.action_confirmation",
"action",
actionConfig.action
),
}))
) {
return;
}
const [domain, service] = actionConfig.service.split(".", 2);
hass.callService(domain, service, actionConfig.service_data);
forwardHaptic("light");
break;
}
case "fire-dom-event": {
fireEvent(node, "ll-custom", actionConfig);
switch (actionConfig.action) {
case "more-info": {
if (config.entity || config.camera_image) {
fireEvent(node, "hass-more-info", {
entityId: config.entity ? config.entity : config.camera_image!,
});
}
break;
}
case "navigate":
if (actionConfig.navigation_path) {
navigate(node, actionConfig.navigation_path);
}
break;
case "url": {
if (actionConfig.url_path) {
window.open(actionConfig.url_path);
}
break;
}
case "toggle": {
if (config.entity) {
toggleEntity(hass, config.entity!);
forwardHaptic("light");
}
break;
}
case "call-service": {
if (!actionConfig.service) {
forwardHaptic("failure");
return;
}
const [domain, service] = actionConfig.service.split(".", 2);
hass.callService(domain, service, actionConfig.service_data);
forwardHaptic("light");
break;
}
case "fire-dom-event": {
fireEvent(node, "ll-custom", actionConfig);
}
}
}
};

View File

@@ -8,7 +8,7 @@ export const handleStructError = (err: Error): string[] => {
for (const failure of err.failures()) {
if (failure.type === "never") {
errors.push(
`Key "${failure.path[0]}" is not supported by the UI editor.`
`Key "${failure.path.join(".")}" is not supported by the UI editor.`
);
} else {
errors.push(

View File

@@ -7,7 +7,7 @@ const isEntityId = (value: unknown, context: StructContext): StructResult => {
if (!value.includes(".")) {
return [
context.fail({
type: "entity id should be in the format 'domain.entity'",
type: "Entity ID should be in the format 'domain.entity'",
}),
];
}
@@ -15,3 +15,16 @@ const isEntityId = (value: unknown, context: StructContext): StructResult => {
};
export const EntityId = struct("entity-id", isEntityId);
const isEntityIdOrAll = (
value: unknown,
context: StructContext
): StructResult => {
if (typeof value === "string" && value === "all") {
return true;
}
return isEntityId(value, context);
};
export const EntityIdOrAll = struct("entity-id-all", isEntityIdOrAll);

View File

@@ -130,6 +130,15 @@ export class HuiActionEditor extends LitElement {
</b>
`
: ""}
${this.config?.action === "multiple"
? html`
<b>
${this.hass!.localize(
"ui.panel.lovelace.editor.action-editor.editor_multiple_actions"
)}
</b>
`
: ""}
`;
}

View File

@@ -164,7 +164,8 @@ class HuiGenericEntityRow extends LitElement {
}
.info {
margin-left: 16px;
flex: 1 0 60px;
margin-right: 8px;
flex: 1 0 30%;
}
.info,
.info > * {
@@ -181,7 +182,6 @@ class HuiGenericEntityRow extends LitElement {
}
.secondary,
ha-relative-time {
display: block;
color: var(--secondary-text-color);
}
state-badge {

View File

@@ -57,7 +57,9 @@ class HuiTimestampDisplay extends LitElement {
}
if (isNaN(this.ts.getTime())) {
return html` Invalid date `;
return html`${this.hass.localize(
"ui.panel.lovelace.components.timestamp-display.invalid"
)}`;
}
const format = this._format;
@@ -68,7 +70,9 @@ class HuiTimestampDisplay extends LitElement {
if (format in FORMATS) {
return html` ${FORMATS[format](this.ts, this.hass.language)} `;
}
return html` Invalid format `;
return html`${this.hass.localize(
"ui.panel.lovelace.components.timestamp-display.invalid_format"
)}`;
}
protected updated(changedProperties: PropertyValues): void {

View File

@@ -71,7 +71,7 @@ const DOMAIN_TO_ELEMENT_TYPE = {
switch: "toggle",
vacuum: "toggle",
// Temporary. Once climate is rewritten,
// water heater should get it's own row.
// water heater should get its own row.
water_heater: "climate",
input_datetime: "input-datetime",
weather: "weather",

View File

@@ -28,7 +28,6 @@ const cardConfigStruct = object({
name: optional(string()),
states: optional(array()),
theme: optional(string()),
layout: optional(object()),
});
const includeDomains = ["alarm_control_panel"];

View File

@@ -37,7 +37,6 @@ const cardConfigStruct = object({
hold_action: optional(actionConfigStruct),
theme: optional(string()),
show_state: optional(boolean()),
layout: optional(object()),
});
const actions = [
@@ -47,6 +46,7 @@ const actions = [
"url",
"call-service",
"none",
"multiple",
];
@customElement("hui-button-card-editor")
@@ -116,7 +116,7 @@ export class HuiButtonCardEditor extends LitElement
.label="${this.hass.localize(
"ui.panel.lovelace.editor.card.generic.entity"
)} (${this.hass.localize(
"ui.panel.lovelace.editor.card.config.required"
"ui.panel.lovelace.editor.card.config.optional"
)})"
.hass=${this.hass}
.value=${this._entity}

View File

@@ -32,7 +32,6 @@ const cardConfigStruct = object({
initial_view: optional(string()),
theme: optional(string()),
entities: array(string()),
layout: optional(object()),
});
const views = ["dayGridMonth", "dayGridDay", "listWeek"];

View File

@@ -36,7 +36,6 @@ const cardConfigStruct = object({
type: string(),
card: any(),
conditions: optional(array(conditionStruct)),
layout: optional(object()),
});
@customElement("hui-conditional-card-editor")

View File

@@ -55,7 +55,6 @@ const cardConfigStruct = object({
entities: array(entitiesConfigStruct),
header: optional(headerFooterConfigStructs),
footer: optional(headerFooterConfigStructs),
layout: optional(object()),
});
@customElement("hui-entities-card-editor")

View File

@@ -32,7 +32,6 @@ const cardConfigStruct = object({
unit: optional(string()),
theme: optional(string()),
footer: optional(headerFooterConfigStructs),
layout: optional(object()),
});
@customElement("hui-entity-card-editor")

View File

@@ -31,7 +31,6 @@ const cardConfigStruct = object({
max: optional(number()),
severity: optional(object()),
theme: optional(string()),
layout: optional(object()),
});
const includeDomains = ["sensor"];

View File

@@ -50,7 +50,6 @@ const cardConfigStruct = object({
show_icon: optional(boolean()),
state_color: optional(boolean()),
entities: array(entitiesConfigStruct),
layout: optional(object()),
});
@customElement("hui-glance-card-editor")

View File

@@ -42,7 +42,6 @@ const cardConfigStruct = object({
title: optional(string()),
hours_to_show: optional(number()),
refresh_interval: optional(number()),
layout: optional(object()),
});
@customElement("hui-history-graph-card-editor")

View File

@@ -23,7 +23,6 @@ const cardConfigStruct = object({
entity: string(),
name: optional(string()),
theme: optional(string()),
layout: optional(object()),
});
const includeDomains = ["humidifier"];

View File

@@ -21,7 +21,6 @@ const cardConfigStruct = object({
title: optional(string()),
url: optional(string()),
aspect_ratio: optional(string()),
layout: optional(object()),
});
@customElement("hui-iframe-card-editor")

View File

@@ -30,7 +30,6 @@ const cardConfigStruct = object({
icon: optional(string()),
hold_action: optional(actionConfigStruct),
double_tap_action: optional(actionConfigStruct),
layout: optional(object()),
});
const includeDomains = ["light"];
@@ -83,6 +82,7 @@ export class HuiLightCardEditor extends LitElement
"url",
"call-service",
"none",
"multiple",
];
return html`

View File

@@ -26,7 +26,6 @@ const cardConfigStruct = object({
title: optional(string()),
hours_to_show: optional(number()),
theme: optional(string()),
layout: optional(object()),
});
@customElement("hui-logbook-card-editor")

View File

@@ -46,7 +46,6 @@ const cardConfigStruct = object({
entities: array(entitiesConfigStruct),
hours_to_show: optional(number()),
geo_location_sources: optional(array()),
layout: optional(object()),
});
@customElement("hui-map-card-editor")

View File

@@ -23,7 +23,6 @@ const cardConfigStruct = object({
title: optional(string()),
content: string(),
theme: optional(string()),
layout: optional(object()),
});
@customElement("hui-markdown-card-editor")

View File

@@ -1,23 +1,22 @@
import {
customElement,
html,
internalProperty,
LitElement,
property,
internalProperty,
TemplateResult,
} from "lit-element";
import { assert, object, optional, string } from "superstruct";
import { fireEvent } from "../../../../common/dom/fire_event";
import "../../../../components/entity/ha-entity-picker";
import { HomeAssistant } from "../../../../types";
import { MediaControlCardConfig } from "../../cards/types";
import { LovelaceCardEditor } from "../../types";
import { EditorTarget, EntitiesEditorEvent } from "../types";
import { assert, object, string, optional } from "superstruct";
const cardConfigStruct = object({
type: string(),
entity: optional(string()),
layout: optional(object()),
});
const includeDomains = ["media_player"];

View File

@@ -25,7 +25,6 @@ const cardConfigStruct = object({
tap_action: optional(actionConfigStruct),
hold_action: optional(actionConfigStruct),
theme: optional(string()),
layout: optional(object()),
});
@customElement("hui-picture-card-editor")
@@ -61,7 +60,7 @@ export class HuiPictureCardEditor extends LitElement
return html``;
}
const actions = ["navigate", "url", "call-service", "none"];
const actions = ["navigate", "url", "call-service", "none", "multiple"];
return html`
<div class="card-config">

View File

@@ -39,7 +39,6 @@ const cardConfigStruct = object({
show_name: optional(boolean()),
show_state: optional(boolean()),
theme: optional(string()),
layout: optional(object()),
});
const includeDomains = ["camera"];
@@ -105,7 +104,14 @@ export class HuiPictureEntityCardEditor extends LitElement
return html``;
}
const actions = ["more-info", "toggle", "navigate", "call-service", "none"];
const actions = [
"more-info",
"toggle",
"navigate",
"call-service",
"none",
"multiple",
];
const views = ["auto", "live"];
const dir = computeRTLDirection(this.hass!);

View File

@@ -42,7 +42,6 @@ const cardConfigStruct = object({
hold_action: optional(actionConfigStruct),
entities: array(entitiesConfigStruct),
theme: optional(string()),
layout: optional(object()),
});
const includeDomains = ["camera"];
@@ -109,7 +108,14 @@ export class HuiPictureGlanceCardEditor extends LitElement
return html``;
}
const actions = ["more-info", "toggle", "navigate", "call-service", "none"];
const actions = [
"more-info",
"toggle",
"navigate",
"call-service",
"none",
"multiple",
];
const views = ["auto", "live"];
return html`

View File

@@ -24,7 +24,6 @@ const cardConfigStruct = object({
entity: string(),
name: optional(string()),
theme: optional(string()),
layout: optional(object()),
});
const includeDomains = ["plant"];

View File

@@ -35,7 +35,6 @@ const cardConfigStruct = object({
detail: optional(number()),
theme: optional(string()),
hours_to_show: optional(number()),
layout: optional(object()),
});
const includeDomains = ["sensor"];

View File

@@ -4,12 +4,11 @@ import {
CSSResult,
customElement,
html,
internalProperty,
LitElement,
property,
internalProperty,
TemplateResult,
} from "lit-element";
import { assert, object, optional, string } from "superstruct";
import { isComponentLoaded } from "../../../../common/config/is_component_loaded";
import { fireEvent } from "../../../../common/dom/fire_event";
import { HomeAssistant } from "../../../../types";
@@ -17,12 +16,12 @@ import { ShoppingListCardConfig } from "../../cards/types";
import "../../components/hui-theme-select-editor";
import { LovelaceCardEditor } from "../../types";
import { EditorTarget, EntitiesEditorEvent } from "../types";
import { string, assert, object, optional } from "superstruct";
const cardConfigStruct = object({
type: string(),
title: optional(string()),
theme: optional(string()),
layout: optional(object()),
});
@customElement("hui-shopping-list-card-editor")

View File

@@ -16,11 +16,11 @@ import {
any,
array,
assert,
boolean,
number,
object,
optional,
string,
boolean,
number,
} from "superstruct";
import { fireEvent, HASSDomEvent } from "../../../../common/dom/fire_event";
import { LovelaceCardConfig, LovelaceConfig } from "../../../../data/lovelace";
@@ -39,7 +39,6 @@ const cardConfigStruct = object({
title: optional(string()),
square: optional(boolean()),
columns: optional(number()),
layout: optional(object()),
});
@customElement("hui-stack-card-editor")

View File

@@ -23,7 +23,6 @@ const cardConfigStruct = object({
entity: string(),
name: optional(string()),
theme: optional(string()),
layout: optional(object()),
});
const includeDomains = ["climate"];

View File

@@ -10,10 +10,10 @@ import {
import { assert, boolean, object, optional, string } from "superstruct";
import { fireEvent } from "../../../../common/dom/fire_event";
import { computeRTLDirection } from "../../../../common/util/compute_rtl";
import "../../../../components/entity/ha-entity-attribute-picker";
import "../../../../components/entity/ha-entity-picker";
import "../../../../components/ha-formfield";
import "../../../../components/ha-switch";
import "../../../../components/entity/ha-entity-attribute-picker";
import { HomeAssistant } from "../../../../types";
import { WeatherForecastCardConfig } from "../../cards/types";
import "../../components/hui-theme-select-editor";
@@ -28,7 +28,6 @@ const cardConfigStruct = object({
theme: optional(string()),
show_forecast: optional(boolean()),
secondary_info_attribute: optional(string()),
layout: optional(object()),
});
const includeDomains = ["weather"];

View File

@@ -98,6 +98,7 @@ export const actionConfigStruct = object({
url_path: optional(string()),
service: optional(string()),
service_data: optional(object()),
actions: optional(array()),
});
const buttonEntitiesRowConfigStruct = object({

View File

@@ -48,9 +48,9 @@ export class HuiServiceButtonElement extends LitElement
return html`
<ha-call-service-button
.hass=${this.hass}
.domain="${this._domain}"
.service="${this._service}"
.serviceData="${this._config.service_data}"
.domain=${this._domain}
.service=${this._service}
.serviceData=${this._config.service_data}
>${this._config.title}</ha-call-service-button
>
`;

View File

@@ -25,7 +25,7 @@ import { handleAction } from "../common/handle-action";
import { UNAVAILABLE_STATES } from "../../../data/entity";
interface SensorEntityConfig extends EntitiesCardEntityConfig {
format?: "relative" | "date" | "time" | "datetime";
format?: "relative" | "total" | "date" | "time" | "datetime";
}
@customElement("hui-sensor-entity-row")

View File

@@ -173,8 +173,8 @@ class LovelaceFullConfigEditor extends LitElement {
text: this.hass.localize(
"ui.panel.lovelace.editor.raw_editor.confirm_unsaved_changes"
),
dismissText: this.hass!.localize("ui.common.leave"),
confirmText: this.hass!.localize("ui.common.stay"),
dismissText: this.hass!.localize("ui.common.stay"),
confirmText: this.hass!.localize("ui.common.leave"),
}))
) {
return;

View File

@@ -33,7 +33,8 @@ export default <T extends Constructor<HassBaseEl>>(superClass: T) =>
duration: 0,
dismissable: false,
action: {
text: this.hass!.localize("ui.notification_toast.dismiss"),
text:
this.hass!.localize("ui.notification_toast.dismiss") || "Dismiss",
action: () => {},
},
});

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