Compare commits

..

1 Commits

Author SHA1 Message Date
Zack Arnett cefb3c3f01 Layout Structs 2020-12-12 16:00:14 -06:00
133 changed files with 2264 additions and 4560 deletions
+5 -5
View File
@@ -74,12 +74,12 @@ DO NOT DELETE ANY TEXT from this template! Otherwise, your issue may be closed w
```
## Problem-relevant frontend configuration
## Problem-relevant configuration
<!--
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.
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.
-->
```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.
-->
+1 -1
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`
- Supervisor: [Instructions](https://developers.home-assistant.io/docs/supervisor/developing)
- Hass.io: [Instructions](https://developers.home-assistant.io/docs/en/hassio_hass.html)
## Frontend development
+16 -1
View File
@@ -36,7 +36,6 @@ 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",
@@ -132,6 +131,22 @@ 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"
+22 -25
View File
@@ -9,29 +9,30 @@ class DemoMoreInfo extends PolymerElement {
static get template() {
return html`
<style>
.root {
:host {
display: flex;
align-items: start;
}
#card {
max-width: 400px;
width: 100vw;
}
ha-card {
width: 352px;
width: 333px;
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) {
.root {
:host {
flex-direction: column;
}
pre {
@@ -39,25 +40,21 @@ class DemoMoreInfo extends PolymerElement {
}
}
</style>
<div class="root">
<div id="card">
<ha-card>
<state-card-content
state-obj="[[_stateObj]]"
hass="[[hass]]"
in-dialog
></state-card-content>
<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>
</div>
<template is="dom-if" if="[[showConfig]]">
<pre>[[_jsonEntity(_stateObj)]]</pre>
</template>
</div>
<more-info-content
hass="[[hass]]"
state-obj="[[_stateObj]]"
></more-info-content>
</ha-card>
<template is="dom-if" if="[[showConfig]]">
<pre>[[_jsonEntity(_stateObj)]]</pre>
</template>
`;
}
+9 -36
View File
@@ -3,18 +3,12 @@ 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;
@@ -29,31 +23,20 @@ class DemoMoreInfos extends PolymerElement {
.filters {
margin-left: 60px;
}
ha-formfield {
margin-right: 16px;
}
</style>
<app-toolbar>
<div class="filters">
<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>
<ha-switch checked="{{_showConfig}}">Show entity</ha-switch>
</div>
</app-toolbar>
<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 class="cards">
<template is="dom-repeat" items="[[entities]]">
<demo-more-info
entity-id="[[item]]"
show-config="[[_showConfig]]"
hass="[[hass]]"
></demo-more-info>
</template>
</div>
`;
}
@@ -68,16 +51,6 @@ 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);
-72
View File
@@ -1,72 +0,0 @@
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",
}),
];
+23 -19
View File
@@ -1,11 +1,6 @@
import {
html,
LitElement,
customElement,
PropertyValues,
query,
TemplateResult,
} from "lit-element";
import { html } from "@polymer/polymer/lib/utils/html-tag";
/* eslint-plugin-disable lit */
import { PolymerElement } from "@polymer/polymer/polymer-element";
import { getEntity } from "../../../src/fake_data/entity";
import { provideHass } from "../../../src/fake_data/provide_hass";
import "../components/demo-cards";
@@ -76,19 +71,28 @@ const CONFIGS = [
},
];
@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>`;
class DemoAlarmPanelEntity extends PolymerElement {
static get template() {
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");
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");
hass.addEntities(ENTITIES);
}
}
+18 -18
View File
@@ -1,11 +1,6 @@
import {
html,
LitElement,
customElement,
PropertyValues,
query,
TemplateResult,
} from "lit-element";
import { html } from "@polymer/polymer/lib/utils/html-tag";
/* eslint-plugin-disable lit */
import { PolymerElement } from "@polymer/polymer/polymer-element";
import { getEntity } from "../../../src/fake_data/entity";
import { provideHass } from "../../../src/fake_data/provide_hass";
import "../components/demo-cards";
@@ -58,19 +53,24 @@ const CONFIGS = [
},
];
@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>`;
class DemoConditional extends PolymerElement {
static get template() {
return html` <demo-cards id="demos" configs="[[_configs]]"></demo-cards> `;
}
protected firstUpdated(changedProperties: PropertyValues) {
super.firstUpdated(changedProperties);
const hass = provideHass(this._demoRoot);
static get properties() {
return {
_configs: {
type: Object,
value: CONFIGS,
},
};
}
public ready() {
super.ready();
const hass = provideHass(this.$.demos);
hass.updateTranslations(null, "en");
hass.updateTranslations("lovelace", "en");
hass.addEntities(ENTITIES);
}
}
+18 -18
View File
@@ -1,11 +1,6 @@
import {
html,
LitElement,
customElement,
PropertyValues,
query,
TemplateResult,
} from "lit-element";
import { html } from "@polymer/polymer/lib/utils/html-tag";
/* eslint-plugin-disable lit */
import { PolymerElement } from "@polymer/polymer/polymer-element";
import { getEntity } from "../../../src/fake_data/entity";
import { provideHass } from "../../../src/fake_data/provide_hass";
import "../components/demo-cards";
@@ -222,19 +217,24 @@ const CONFIGS = [
},
];
@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>`;
class DemoEntities extends PolymerElement {
static get template() {
return html` <demo-cards id="demos" configs="[[_configs]]"></demo-cards> `;
}
protected firstUpdated(changedProperties: PropertyValues) {
super.firstUpdated(changedProperties);
const hass = provideHass(this._demoRoot);
static get properties() {
return {
_configs: {
type: Object,
value: CONFIGS,
},
};
}
public ready() {
super.ready();
const hass = provideHass(this.$.demos);
hass.updateTranslations(null, "en");
hass.updateTranslations("lovelace", "en");
hass.addEntities(ENTITIES);
}
}
@@ -1,11 +1,6 @@
import {
html,
LitElement,
customElement,
PropertyValues,
query,
TemplateResult,
} from "lit-element";
import { html } from "@polymer/polymer/lib/utils/html-tag";
/* eslint-plugin-disable lit */
import { PolymerElement } from "@polymer/polymer/polymer-element";
import { getEntity } from "../../../src/fake_data/entity";
import { provideHass } from "../../../src/fake_data/provide_hass";
import "../components/demo-cards";
@@ -74,19 +69,24 @@ const CONFIGS = [
},
];
@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>`;
class DemoButtonEntity extends PolymerElement {
static get template() {
return html` <demo-cards id="demos" configs="[[_configs]]"></demo-cards> `;
}
protected firstUpdated(changedProperties: PropertyValues) {
super.firstUpdated(changedProperties);
const hass = provideHass(this._demoRoot);
static get properties() {
return {
_configs: {
type: Object,
value: CONFIGS,
},
};
}
public ready() {
super.ready();
const hass = provideHass(this.$.demos);
hass.updateTranslations(null, "en");
hass.updateTranslations("lovelace", "en");
hass.addEntities(ENTITIES);
}
}
@@ -1,11 +1,6 @@
import {
html,
LitElement,
customElement,
PropertyValues,
query,
TemplateResult,
} from "lit-element";
import { html } from "@polymer/polymer/lib/utils/html-tag";
/* eslint-plugin-disable lit */
import { PolymerElement } from "@polymer/polymer/polymer-element";
import { getEntity } from "../../../src/fake_data/entity";
import { provideHass } from "../../../src/fake_data/provide_hass";
import "../components/demo-cards";
@@ -94,21 +89,26 @@ const CONFIGS = [
},
];
@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>`;
class DemoFilter extends PolymerElement {
static get template() {
return html` <demo-cards id="demos" configs="[[_configs]]"></demo-cards> `;
}
protected firstUpdated(changedProperties: PropertyValues) {
super.firstUpdated(changedProperties);
const hass = provideHass(this._demoRoot);
static get properties() {
return {
_configs: {
type: Object,
value: CONFIGS,
},
};
}
public ready() {
super.ready();
const hass = provideHass(this.$.demos);
hass.updateTranslations(null, "en");
hass.updateTranslations("lovelace", "en");
hass.addEntities(ENTITIES);
}
}
customElements.define("demo-hui-entity-filter-card", DemoEntityFilter);
customElements.define("demo-hui-entity-filter-card", DemoFilter);
+18 -18
View File
@@ -1,11 +1,6 @@
import {
html,
LitElement,
customElement,
PropertyValues,
query,
TemplateResult,
} from "lit-element";
import { html } from "@polymer/polymer/lib/utils/html-tag";
/* eslint-plugin-disable lit */
import { PolymerElement } from "@polymer/polymer/polymer-element";
import { getEntity } from "../../../src/fake_data/entity";
import { provideHass } from "../../../src/fake_data/provide_hass";
import "../components/demo-cards";
@@ -112,19 +107,24 @@ const CONFIGS = [
},
];
@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>`;
class DemoGaugeEntity extends PolymerElement {
static get template() {
return html` <demo-cards id="demos" configs="[[_configs]]"></demo-cards> `;
}
protected firstUpdated(changedProperties: PropertyValues) {
super.firstUpdated(changedProperties);
const hass = provideHass(this._demoRoot);
static get properties() {
return {
_configs: {
type: Object,
value: CONFIGS,
},
};
}
public ready() {
super.ready();
const hass = provideHass(this.$.demos);
hass.updateTranslations(null, "en");
hass.updateTranslations("lovelace", "en");
hass.addEntities(ENTITIES);
}
}
+19 -19
View File
@@ -1,11 +1,6 @@
import {
html,
LitElement,
customElement,
PropertyValues,
query,
TemplateResult,
} from "lit-element";
import { html } from "@polymer/polymer/lib/utils/html-tag";
/* eslint-plugin-disable lit */
import { PolymerElement } from "@polymer/polymer/polymer-element";
import { getEntity } from "../../../src/fake_data/entity";
import { provideHass } from "../../../src/fake_data/provide_hass";
import "../components/demo-cards";
@@ -223,21 +218,26 @@ const CONFIGS = [
},
];
@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>`;
class DemoPicEntity extends PolymerElement {
static get template() {
return html` <demo-cards id="demos" configs="[[_configs]]"></demo-cards> `;
}
protected firstUpdated(changedProperties: PropertyValues) {
super.firstUpdated(changedProperties);
const hass = provideHass(this._demoRoot);
static get properties() {
return {
_configs: {
type: Object,
value: CONFIGS,
},
};
}
public ready() {
super.ready();
const hass = provideHass(this.$.demos);
hass.updateTranslations(null, "en");
hass.updateTranslations("lovelace", "en");
hass.addEntities(ENTITIES);
}
}
customElements.define("demo-hui-glance-card", DemoGlanceEntity);
customElements.define("demo-hui-glance-card", DemoPicEntity);
+15 -5
View File
@@ -1,4 +1,6 @@
import { html, LitElement, customElement, TemplateResult } from "lit-element";
import { html } from "@polymer/polymer/lib/utils/html-tag";
/* eslint-plugin-disable lit */
import { PolymerElement } from "@polymer/polymer/polymer-element";
import "../components/demo-cards";
const CONFIGS = [
@@ -35,10 +37,18 @@ const CONFIGS = [
},
];
@customElement("demo-hui-iframe-card")
class DemoIframe extends LitElement {
protected render(): TemplateResult {
return html`<demo-cards id="demos" .configs=${CONFIGS}></demo-cards>`;
class DemoIframe extends PolymerElement {
static get template() {
return html` <demo-cards configs="[[_configs]]"></demo-cards> `;
}
static get properties() {
return {
_configs: {
type: Object,
value: CONFIGS,
},
};
}
}
+18 -18
View File
@@ -1,11 +1,6 @@
import {
html,
LitElement,
customElement,
PropertyValues,
query,
TemplateResult,
} from "lit-element";
import { html } from "@polymer/polymer/lib/utils/html-tag";
/* eslint-plugin-disable lit */
import { PolymerElement } from "@polymer/polymer/polymer-element";
import { getEntity } from "../../../src/fake_data/entity";
import { provideHass } from "../../../src/fake_data/provide_hass";
import "../components/demo-cards";
@@ -68,19 +63,24 @@ const CONFIGS = [
},
];
@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>`;
class DemoLightEntity extends PolymerElement {
static get template() {
return html` <demo-cards id="demos" configs="[[_configs]]"></demo-cards> `;
}
protected firstUpdated(changedProperties: PropertyValues) {
super.firstUpdated(changedProperties);
const hass = provideHass(this._demoRoot);
static get properties() {
return {
_configs: {
type: Object,
value: CONFIGS,
},
};
}
public ready() {
super.ready();
const hass = provideHass(this.$.demos);
hass.updateTranslations(null, "en");
hass.updateTranslations("lovelace", "en");
hass.addEntities(ENTITIES);
}
}
+18 -18
View File
@@ -1,11 +1,6 @@
import {
html,
LitElement,
customElement,
PropertyValues,
query,
TemplateResult,
} from "lit-element";
import { html } from "@polymer/polymer/lib/utils/html-tag";
/* eslint-plugin-disable lit */
import { PolymerElement } from "@polymer/polymer/polymer-element";
import { getEntity } from "../../../src/fake_data/entity";
import { provideHass } from "../../../src/fake_data/provide_hass";
import "../components/demo-cards";
@@ -166,19 +161,24 @@ const CONFIGS = [
},
];
@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>`;
class DemoMap extends PolymerElement {
static get template() {
return html` <demo-cards id="demos" configs="[[_configs]]"></demo-cards> `;
}
protected firstUpdated(changedProperties: PropertyValues) {
super.firstUpdated(changedProperties);
const hass = provideHass(this._demoRoot);
static get properties() {
return {
_configs: {
type: Object,
value: CONFIGS,
},
};
}
public ready() {
super.ready();
const hass = provideHass(this.$.demos);
hass.updateTranslations(null, "en");
hass.updateTranslations("lovelace", "en");
hass.addEntities(ENTITIES);
}
}
+18 -19
View File
@@ -1,11 +1,6 @@
import {
html,
LitElement,
customElement,
PropertyValues,
query,
TemplateResult,
} from "lit-element";
import { html } from "@polymer/polymer/lib/utils/html-tag";
/* eslint-plugin-disable lit */
import { PolymerElement } from "@polymer/polymer/polymer-element";
import { mockTemplate } from "../../../demo/src/stubs/template";
import { provideHass } from "../../../src/fake_data/provide_hass";
import "../components/demo-cards";
@@ -259,19 +254,23 @@ const CONFIGS = [
},
];
@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>`;
class DemoMarkdown extends PolymerElement {
static get template() {
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");
static get properties() {
return {
_configs: {
type: Object,
value: CONFIGS,
},
};
}
public ready() {
super.ready();
const hass = provideHass(this.$.demos);
mockTemplate(hass);
}
}
@@ -1,11 +1,6 @@
import {
html,
LitElement,
customElement,
PropertyValues,
query,
TemplateResult,
} from "lit-element";
import { html } from "@polymer/polymer/lib/utils/html-tag";
/* eslint-plugin-disable lit */
import { PolymerElement } from "@polymer/polymer/polymer-element";
import { provideHass } from "../../../src/fake_data/provide_hass";
import "../components/demo-cards";
import { createMediaPlayerEntities } from "../data/media_players";
@@ -163,21 +158,26 @@ const CONFIGS = [
},
];
@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>`;
class DemoHuiMediControlCard extends PolymerElement {
static get template() {
return html` <demo-cards id="demos" configs="[[_configs]]"></demo-cards> `;
}
protected firstUpdated(changedProperties: PropertyValues) {
super.firstUpdated(changedProperties);
const hass = provideHass(this._demoRoot);
static get properties() {
return {
_configs: {
type: Object,
value: CONFIGS,
},
};
}
public ready() {
super.ready();
const hass = provideHass(this.$.demos);
hass.updateTranslations(null, "en");
hass.updateTranslations("lovelace", "en");
hass.addEntities(createMediaPlayerEntities());
}
}
customElements.define("demo-hui-media-control-card", DemoHuiMediaControlCard);
customElements.define("demo-hui-media-control-card", DemoHuiMediControlCard);
+19 -19
View File
@@ -1,11 +1,6 @@
import {
html,
LitElement,
customElement,
PropertyValues,
query,
TemplateResult,
} from "lit-element";
import { html } from "@polymer/polymer/lib/utils/html-tag";
/* eslint-plugin-disable lit */
import { PolymerElement } from "@polymer/polymer/polymer-element";
import { provideHass } from "../../../src/fake_data/provide_hass";
import "../components/demo-cards";
import { createMediaPlayerEntities } from "../data/media_players";
@@ -60,21 +55,26 @@ const CONFIGS = [
},
];
@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>`;
class DemoHuiMediaPlayerRows extends PolymerElement {
static get template() {
return html` <demo-cards id="demos" configs="[[_configs]]"></demo-cards> `;
}
protected firstUpdated(changedProperties: PropertyValues) {
super.firstUpdated(changedProperties);
const hass = provideHass(this._demoRoot);
static get properties() {
return {
_configs: {
type: Object,
value: CONFIGS,
},
};
}
public ready() {
super.ready();
const hass = provideHass(this.$.demos);
hass.updateTranslations(null, "en");
hass.updateTranslations("lovelace", "en");
hass.addEntities(createMediaPlayerEntities());
}
}
customElements.define("demo-hui-media-player-row", DemoHuiMediaPlayerRow);
customElements.define("demo-hui-media-player-rows", DemoHuiMediaPlayerRows);
@@ -1,11 +1,6 @@
import {
html,
LitElement,
customElement,
PropertyValues,
query,
TemplateResult,
} from "lit-element";
import { html } from "@polymer/polymer/lib/utils/html-tag";
/* eslint-plugin-disable lit */
import { PolymerElement } from "@polymer/polymer/polymer-element";
import { getEntity } from "../../../src/fake_data/entity";
import { provideHass } from "../../../src/fake_data/provide_hass";
import "../components/demo-cards";
@@ -130,21 +125,26 @@ const CONFIGS = [
},
];
@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>`;
class DemoPicElements extends PolymerElement {
static get template() {
return html` <demo-cards id="demos" configs="[[_configs]]"></demo-cards> `;
}
protected firstUpdated(changedProperties: PropertyValues) {
super.firstUpdated(changedProperties);
const hass = provideHass(this._demoRoot);
static get properties() {
return {
_configs: {
type: Object,
value: CONFIGS,
},
};
}
public ready() {
super.ready();
const hass = provideHass(this.$.demos);
hass.updateTranslations(null, "en");
hass.updateTranslations("lovelace", "en");
hass.addEntities(ENTITIES);
}
}
customElements.define("demo-hui-picture-elements-card", DemoPictureElements);
customElements.define("demo-hui-picture-elements-card", DemoPicElements);
@@ -1,11 +1,6 @@
import {
html,
LitElement,
customElement,
PropertyValues,
query,
TemplateResult,
} from "lit-element";
import { html } from "@polymer/polymer/lib/utils/html-tag";
/* eslint-plugin-disable lit */
import { PolymerElement } from "@polymer/polymer/polymer-element";
import { getEntity } from "../../../src/fake_data/entity";
import { provideHass } from "../../../src/fake_data/provide_hass";
import "../components/demo-cards";
@@ -85,21 +80,26 @@ const CONFIGS = [
},
];
@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>`;
class DemoPicEntity extends PolymerElement {
static get template() {
return html` <demo-cards id="demos" configs="[[_configs]]"></demo-cards> `;
}
protected firstUpdated(changedProperties: PropertyValues) {
super.firstUpdated(changedProperties);
const hass = provideHass(this._demoRoot);
static get properties() {
return {
_configs: {
type: Object,
value: CONFIGS,
},
};
}
public ready() {
super.ready();
const hass = provideHass(this.$.demos);
hass.updateTranslations(null, "en");
hass.updateTranslations("lovelace", "en");
hass.addEntities(ENTITIES);
}
}
customElements.define("demo-hui-picture-entity-card", DemoPictureEntity);
customElements.define("demo-hui-picture-entity-card", DemoPicEntity);
@@ -1,11 +1,6 @@
import {
html,
LitElement,
customElement,
PropertyValues,
query,
TemplateResult,
} from "lit-element";
import { html } from "@polymer/polymer/lib/utils/html-tag";
/* eslint-plugin-disable lit */
import { PolymerElement } from "@polymer/polymer/polymer-element";
import { getEntity } from "../../../src/fake_data/entity";
import { provideHass } from "../../../src/fake_data/provide_hass";
import "../components/demo-cards";
@@ -126,21 +121,26 @@ const CONFIGS = [
},
];
@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>`;
class DemoPicGlance extends PolymerElement {
static get template() {
return html` <demo-cards id="demos" configs="[[_configs]]"></demo-cards> `;
}
protected firstUpdated(changedProperties: PropertyValues) {
super.firstUpdated(changedProperties);
const hass = provideHass(this._demoRoot);
static get properties() {
return {
_configs: {
type: Object,
value: CONFIGS,
},
};
}
public ready() {
super.ready();
const hass = provideHass(this.$.demos);
hass.updateTranslations(null, "en");
hass.updateTranslations("lovelace", "en");
hass.addEntities(ENTITIES);
}
}
customElements.define("demo-hui-picture-glance-card", DemoPictureGlance);
customElements.define("demo-hui-picture-glance-card", DemoPicGlance);
-55
View File
@@ -1,55 +0,0 @@
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);
@@ -1,11 +1,6 @@
import {
html,
LitElement,
customElement,
PropertyValues,
query,
TemplateResult,
} from "lit-element";
import { html } from "@polymer/polymer/lib/utils/html-tag";
/* eslint-plugin-disable lit */
import { PolymerElement } from "@polymer/polymer/polymer-element";
import { provideHass } from "../../../src/fake_data/provide_hass";
import "../components/demo-cards";
@@ -25,19 +20,24 @@ const CONFIGS = [
},
];
@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>`;
class DemoShoppingListEntity extends PolymerElement {
static get template() {
return html` <demo-cards id="demos" configs="[[_configs]]"></demo-cards> `;
}
protected firstUpdated(changedProperties: PropertyValues) {
super.firstUpdated(changedProperties);
const hass = provideHass(this._demoRoot);
static get properties() {
return {
_configs: {
type: Object,
value: CONFIGS,
},
};
}
public ready() {
super.ready();
const hass = provideHass(this.$.demos);
hass.updateTranslations(null, "en");
hass.updateTranslations("lovelace", "en");
hass.mockAPI("shopping_list", () => [
{ name: "list", id: 1, complete: false },
+18 -18
View File
@@ -1,11 +1,6 @@
import {
html,
LitElement,
customElement,
PropertyValues,
query,
TemplateResult,
} from "lit-element";
import { html } from "@polymer/polymer/lib/utils/html-tag";
/* eslint-plugin-disable lit */
import { PolymerElement } from "@polymer/polymer/polymer-element";
import { mockHistory } from "../../../demo/src/stubs/history";
import { getEntity } from "../../../src/fake_data/entity";
import { provideHass } from "../../../src/fake_data/provide_hass";
@@ -137,19 +132,24 @@ const CONFIGS = [
},
];
@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>`;
class DemoStack extends PolymerElement {
static get template() {
return html` <demo-cards id="demos" configs="[[_configs]]"></demo-cards> `;
}
protected firstUpdated(changedProperties: PropertyValues) {
super.firstUpdated(changedProperties);
const hass = provideHass(this._demoRoot);
static get properties() {
return {
_configs: {
type: Object,
value: CONFIGS,
},
};
}
public ready() {
super.ready();
const hass = provideHass(this.$.demos);
hass.updateTranslations(null, "en");
hass.updateTranslations("lovelace", "en");
hass.addEntities(ENTITIES);
mockHistory(hass);
}
+18 -18
View File
@@ -1,11 +1,6 @@
import {
html,
LitElement,
customElement,
PropertyValues,
query,
TemplateResult,
} from "lit-element";
import { html } from "@polymer/polymer/lib/utils/html-tag";
/* eslint-plugin-disable lit */
import { PolymerElement } from "@polymer/polymer/polymer-element";
import { getEntity } from "../../../src/fake_data/entity";
import { provideHass } from "../../../src/fake_data/provide_hass";
import "../components/demo-cards";
@@ -79,19 +74,24 @@ const CONFIGS = [
},
];
@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>`;
class DemoThermostatEntity extends PolymerElement {
static get template() {
return html` <demo-cards id="demos" configs="[[_configs]]"></demo-cards> `;
}
protected firstUpdated(changedProperties: PropertyValues) {
super.firstUpdated(changedProperties);
const hass = provideHass(this._demoRoot);
static get properties() {
return {
_configs: {
type: Object,
value: CONFIGS,
},
};
}
public ready() {
super.ready();
const hass = provideHass(this.$.demos);
hass.updateTranslations(null, "en");
hass.updateTranslations("lovelace", "en");
hass.addEntities(ENTITIES);
}
}
+27 -58
View File
@@ -1,27 +1,10 @@
import {
html,
LitElement,
customElement,
property,
PropertyValues,
query,
TemplateResult,
} from "lit-element";
import { html } from "@polymer/polymer/lib/utils/html-tag";
/* eslint-plugin-disable lit */
import { PolymerElement } from "@polymer/polymer/polymer-element";
import "../../../src/components/ha-card";
import {
SUPPORT_BRIGHTNESS,
SUPPORT_COLOR_TEMP,
SUPPORT_EFFECT,
SUPPORT_FLASH,
SUPPORT_COLOR,
SUPPORT_TRANSITION,
SUPPORT_WHITE_VALUE,
} from "../../../src/data/light";
import { SUPPORT_BRIGHTNESS } from "../../../src/data/light";
import { getEntity } from "../../../src/fake_data/entity";
import {
provideHass,
MockHomeAssistant,
} from "../../../src/fake_data/provide_hass";
import { provideHass } from "../../../src/fake_data/provide_hass";
import "../components/demo-more-infos";
import "../../../src/dialogs/more-info/more-info-content";
@@ -31,52 +14,38 @@ const ENTITIES = [
}),
getEntity("light", "kitchen_light", "on", {
friendly_name: "Brightness Light",
brightness: 200,
brightness: 80,
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"],
}),
];
@customElement("demo-more-info-light")
class DemoMoreInfoLight extends LitElement {
@property() public hass!: MockHomeAssistant;
@query("demo-more-infos") private _demoRoot!: HTMLElement;
protected render(): TemplateResult {
class DemoMoreInfoLight extends PolymerElement {
static get template() {
return html`
<demo-more-infos
.hass=${this.hass}
.entities=${ENTITIES.map((ent) => ent.entityId)}
hass="[[hass]]"
entities="[[_entities]]"
></demo-more-infos>
`;
}
protected firstUpdated(changedProperties: PropertyValues) {
super.firstUpdated(changedProperties);
const hass = provideHass(this._demoRoot);
hass.updateTranslations(null, "en");
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");
hass.addEntities(ENTITIES);
}
}
+4 -3
View File
@@ -1,10 +1,9 @@
import "@material/mwc-button";
import { customElement, html, LitElement, TemplateResult } from "lit-element";
import { 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`
@@ -21,7 +20,7 @@ export class DemoUtilLongPress extends LitElement {
<textarea></textarea>
<div>Try pressing and scrolling too!</div>
<div>(try pressing and scrolling too!)</div>
</ha-card>
`
)}
@@ -63,3 +62,5 @@ export class DemoUtilLongPress extends LitElement {
`;
}
}
customElements.define("demo-util-long-press", DemoUtilLongPress);
+2
View File
@@ -14,6 +14,8 @@ 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`
@@ -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",
+1 -1
View File
@@ -178,7 +178,7 @@ class HassioSupervisorInfo extends LitElement {
</div>`}
${!this.supervisor.supervisor.healthy
? html`<div class="error">
Your installation is running in an unhealthy state.
Your installtion is running in an unhealthy state.
<button
class="link"
title="Learn more about why your system is marked as unhealthy"
+16 -16
View File
@@ -29,22 +29,22 @@
"@fullcalendar/daygrid": "5.1.0",
"@fullcalendar/interaction": "5.1.0",
"@fullcalendar/list": "5.1.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",
"@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",
"@mdi/js": "5.6.55",
"@mdi/svg": "5.6.55",
"@polymer/app-layout": "^3.0.2",
+1 -1
View File
@@ -2,7 +2,7 @@ from setuptools import setup, find_packages
setup(
name="home-assistant-frontend",
version="20201229.0",
version="20201212.0",
description="The Home Assistant frontend",
url="https://github.com/home-assistant/home-assistant-polymer",
author="The Home Assistant Authors",
+9 -14
View File
@@ -1,17 +1,12 @@
export const copyToClipboard = async (str) => {
export const copyToClipboard = (str) => {
if (navigator.clipboard) {
try {
await navigator.clipboard.writeText(str);
return;
} catch {
// just continue with the fallback coding below
}
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);
}
const el = document.createElement("textarea");
el.value = str;
document.body.appendChild(el);
el.select();
document.execCommand("copy");
document.body.removeChild(el);
};
+1 -1
View File
@@ -99,7 +99,7 @@ export class HaDataTable extends LitElement {
@property({ type: Boolean }) public hasFab = false;
/**
* Add an extra row at the bottom of the data table
* Add an extra rows at the bottom of the datatabel
* @type {TemplateResult}
*/
@property({ attribute: false }) public appendRow?;
@@ -58,14 +58,6 @@ 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;
}
+1 -4
View File
@@ -63,7 +63,7 @@ class HaAttributes extends LitElement {
justify-content: space-between;
}
.data-entry .value {
max-width: 60%;
max-width: 50%;
overflow-wrap: break-word;
text-align: right;
}
@@ -77,9 +77,6 @@ class HaAttributes extends LitElement {
pre {
font-family: inherit;
font-size: inherit;
margin: 0px;
overflow-wrap: break-word;
white-space: pre-line;
}
`;
}
+1 -1
View File
@@ -10,6 +10,7 @@ class HaLabeledSlider extends PolymerElement {
<style>
:host {
display: block;
border-radius: 4px;
}
.title {
@@ -29,7 +30,6 @@ class HaLabeledSlider extends PolymerElement {
ha-slider {
flex-grow: 1;
background-image: var(--ha-slider-background);
border-radius: 4px;
}
</style>
+7 -11
View File
@@ -124,17 +124,13 @@ export const getLogbookMessage = (
switch (domain) {
case "device_tracker":
case "person":
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
);
return state === "not_home"
? hass.localize(`${LOGBOOK_LOCALIZE_PATH}.was_away`)
: hass.localize(
`${LOGBOOK_LOCALIZE_PATH}.was_at_state`,
"state",
state
);
case "sun":
return state === "above_horizon"
+1 -7
View File
@@ -148,11 +148,6 @@ export interface CustomActionConfig extends BaseActionConfig {
action: "fire-dom-event";
}
export interface MultipleActionConfig extends BaseActionConfig {
action: "multiple";
actions: ActionConfig[];
}
export interface BaseActionConfig {
confirmation?: ConfirmationRestrictionConfig;
}
@@ -173,8 +168,7 @@ export type ActionConfig =
| UrlActionConfig
| MoreInfoActionConfig
| NoActionConfig
| CustomActionConfig
| MultipleActionConfig;
| CustomActionConfig;
type LovelaceUpdatedEvent = HassEventBase & {
event_type: "lovelace_updated";
-9
View File
@@ -38,12 +38,3 @@ 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,
});
@@ -10,7 +10,6 @@ import {
TemplateResult,
} from "lit-element";
import { HomeAssistant } from "../../../types";
import { UNAVAILABLE_STATES } from "../../../data/entity";
@customElement("more-info-counter")
class MoreInfoCounter extends LitElement {
@@ -23,29 +22,21 @@ 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}
.disabled=${disabled}
@click="${this._handleActionClick}"
>
${this.hass!.localize("ui.card.counter.actions.increment")}
</mwc-button>
<mwc-button
.action="${"decrement"}"
@click=${this._handleActionClick}
.disabled=${disabled}
@click="${this._handleActionClick}"
>
${this.hass!.localize("ui.card.counter.actions.decrement")}
</mwc-button>
<mwc-button
.action="${"reset"}"
@click=${this._handleActionClick}
.disabled=${disabled}
>
<mwc-button .action="${"reset"}" @click="${this._handleActionClick}">
${this.hass!.localize("ui.card.counter.actions.reset")}
</mwc-button>
</div>
@@ -62,7 +53,8 @@ class MoreInfoCounter extends LitElement {
static get styles(): CSSResult {
return css`
.actions {
margin: 8px 0;
margin: 0;
padding-top: 20px;
display: flex;
flex-wrap: wrap;
justify-content: center;
@@ -75,7 +75,7 @@ class MoreInfoPerson extends LitElement {
justify-content: space-between;
}
.actions {
margin: 8px 0;
margin: 36px 0 8px 0;
text-align: right;
}
ha-map {
@@ -80,7 +80,6 @@ class MoreInfoSun extends LitElement {
}
ha-relative-time {
display: inline-block;
white-space: nowrap;
}
ha-relative-time::first-letter {
text-transform: lowercase;
@@ -76,7 +76,8 @@ class MoreInfoTimer extends LitElement {
static get styles(): CSSResult {
return css`
.actions {
margin: 8px 0;
margin: 0;
padding-top: 20px;
display: flex;
flex-wrap: wrap;
justify-content: center;
+1 -1
View File
@@ -61,7 +61,7 @@ export class HaTabsSubpageDataTable extends LitElement {
@property({ type: Boolean }) public hasFab = false;
/**
* Add an extra row at the bottom of the data table
* Add an extra rows at the bottom of the datatabel
* @type {TemplateResult}
*/
@property({ attribute: false }) public appendRow?;
@@ -15,14 +15,12 @@ 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";
@@ -105,8 +103,6 @@ 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;
@@ -115,10 +111,6 @@ 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() {
@@ -195,7 +187,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
@@ -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 { EntityIdOrAll } from "../../../../lovelace/common/structs/is-entity-id";
import { EntityId } from "../../../../lovelace/common/structs/is-entity-id";
import { ActionElement, handleChangeEvent } from "../ha-automation-action-row";
const actionStruct = object({
service: optional(string()),
entity_id: optional(EntityIdOrAll),
entity_id: optional(EntityId),
data: optional(any()),
});
@@ -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>`
@@ -395,12 +395,9 @@ export class HaAutomationEditor extends KeyboardShortcutMixin(LitElement) {
return cleanConfig;
}
private async _copyYaml(): Promise<void> {
private async _copyYaml() {
if (this._editor?.yaml) {
await copyToClipboard(this._editor.yaml);
showToast(this, {
message: this.hass.localize("ui.common.copied_clipboard"),
});
copyToClipboard(this._editor.yaml);
}
}
@@ -92,7 +92,7 @@ class HaConfigCustomize extends LocalizeMixin(PolymerElement) {
}
_computeTabs() {
return configSections.advanced;
return configSections.general;
}
computeEntities(hass) {
@@ -189,7 +189,9 @@ export class HaConfigDeviceDashboard extends LitElement {
),
model: device.model || "<unknown>",
manufacturer: device.manufacturer || "<unknown>",
area: device.area_id ? areaLookup[device.area_id].name : undefined,
area: device.area_id
? areaLookup[device.area_id].name
: this.hass.localize("ui.panel.config.devices.data_table.no_area"),
integration: device.config_entries.length
? device.config_entries
.filter((entId) => entId in entryLookup)
+3 -5
View File
@@ -197,7 +197,7 @@ class SystemHealthCard extends LitElement {
});
}
private async _copyInfo(ev: CustomEvent<ActionDetail>): Promise<void> {
private _copyInfo(ev: CustomEvent<ActionDetail>): void {
const github = ev.detail.index === 1;
let haContent: string | undefined;
const domainParts: string[] = [];
@@ -250,15 +250,13 @@ class SystemHealthCard extends LitElement {
}
}
await copyToClipboard(
copyToClipboard(
`${github ? "## " : ""}System Health\n${haContent}\n\n${domainParts.join(
"\n\n"
)}`
);
showToast(this, {
message: this.hass.localize("ui.common.copied_clipboard"),
});
showToast(this, { message: this.hass.localize("ui.common.copied") });
}
static get styles(): CSSResult {
@@ -141,15 +141,11 @@ class OZWNodeDashboard extends LitElement {
${this._metadata.metadata.ResetHelp}
</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>
`
: ``}
<ha-card class="content" header="WakeUp">
<div class="card-content">
${this._metadata.metadata.WakeupHelp}
</div>
</ha-card>
`
: ``}
`
@@ -203,10 +199,6 @@ class OZWNodeDashboard extends LitElement {
margin-top: 24px;
}
.content:last-child {
margin-bottom: 24px;
}
.sectionHeader {
position: relative;
padding-right: 40px;
@@ -49,17 +49,19 @@ class ZHADevicePairingStatusCard extends LitElement {
class="discovered ${classMap({
initialized: this.device.pairing_status === INITIALIZED,
})}"
><div class="header">
<h4>
><div
class="header"
>
<h1>
${this.hass!.localize(
`ui.panel.config.zha.device_pairing_card.${this.device.pairing_status}`
)}
</h4>
<h1>
</h1>
<h4>
${this.hass!.localize(
`ui.panel.config.zha.device_pairing_card.${this.device.pairing_status}_status_text`
)}
</h1>
</h4>
</div>
<div class="card-content">
${[INTERVIEW_COMPLETE, CONFIGURED].includes(
@@ -15,12 +15,6 @@ 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 {
@@ -34,21 +28,9 @@ 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) {
@@ -109,27 +91,6 @@ 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>
`;
@@ -140,18 +101,15 @@ 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[]) {
this._nodes = [];
const nodes: Node[] = [];
const edges: Edge[] = [];
devices.forEach((device) => {
this._nodes.push({
nodes.push({
id: device.ieee,
label: this._buildLabel(device),
shape: this._getShape(device),
@@ -179,7 +137,7 @@ export class ZHANetworkVisualizationPage extends LitElement {
}
});
this._network?.setData({ nodes: this._nodes, edges: edges });
this._network?.setData({ nodes: nodes, edges: edges });
}
private _getLQI(lqi: number): EdgeOptions["color"] {
@@ -223,7 +181,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>${formatAsPaddedHex(device.nwk)}`;
label += `\n<b>NWK: </b>${device.nwk}`;
}
if (device.manufacturer != null && device.model != null) {
label += `\n<b>Device: </b>${device.manufacturer} ${device.model}`;
@@ -236,56 +194,6 @@ 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`
@@ -300,30 +208,6 @@ 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;
}
`,
];
}
@@ -2,6 +2,7 @@ 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,
@@ -9,10 +10,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 {
@@ -26,7 +27,6 @@ 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,6 +35,8 @@ 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;
@@ -81,6 +83,15 @@ 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>
@@ -151,15 +162,23 @@ class DialogSystemLogDetail extends LitElement {
}
}
private async _copyLog(): Promise<void> {
private _copyLog(): void {
const copyElement = this.shadowRoot?.querySelector(
".contents"
) as HTMLElement;
await copyToClipboard(copyElement.innerText);
showToast(this, {
message: this.hass.localize("ui.common.copied_clipboard"),
});
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);
}
static get styles(): CSSResult[] {
+2 -5
View File
@@ -544,12 +544,9 @@ export class HaScriptEditor extends KeyboardShortcutMixin(LitElement) {
return this._config;
}
private async _copyYaml(): Promise<void> {
private async _copyYaml() {
if (this._editor?.yaml) {
await copyToClipboard(this._editor.yaml);
showToast(this, {
message: this.hass.localize("ui.common.copied_clipboard"),
});
copyToClipboard(this._editor.yaml);
}
}
+17 -11
View File
@@ -119,21 +119,27 @@ class HaPanelHistory extends LitElement {
todayEnd.setDate(todayEnd.getDate() + 1);
todayEnd.setMilliseconds(todayEnd.getMilliseconds() - 1);
const yesterday = new Date(today);
yesterday.setDate(today.getDate() - 1);
const yesterdayEnd = new Date(today);
const todayCopy = new Date(today);
const yesterday = new Date(todayCopy.setDate(today.getDate() - 1));
const yesterdayEnd = new Date(yesterday);
yesterdayEnd.setDate(yesterdayEnd.getDate() + 1);
yesterdayEnd.setMilliseconds(yesterdayEnd.getMilliseconds() - 1);
const thisWeekStart = new Date(today);
thisWeekStart.setDate(today.getDate() - today.getDay());
const thisWeekEnd = new Date(thisWeekStart);
thisWeekEnd.setDate(thisWeekStart.getDate() + 7);
const thisWeekStart = new Date(
todayCopy.setDate(today.getDate() - today.getDay())
);
const thisWeekEnd = new Date(
todayCopy.setDate(thisWeekStart.getDate() + 7)
);
thisWeekEnd.setMilliseconds(thisWeekEnd.getMilliseconds() - 1);
const lastWeekStart = new Date(today);
lastWeekStart.setDate(today.getDate() - today.getDay() - 7);
const lastWeekEnd = new Date(lastWeekStart);
lastWeekEnd.setDate(lastWeekStart.getDate() + 7);
const lastWeekStart = new Date(
todayCopy.setDate(today.getDate() - today.getDay() - 7)
);
const lastWeekEnd = new Date(
todayCopy.setDate(lastWeekStart.getDate() + 7)
);
lastWeekEnd.setMilliseconds(lastWeekEnd.getMilliseconds() - 1);
this._ranges = {
+1 -1
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 allow-modals"
sandbox="allow-forms allow-popups allow-pointer-lock allow-same-origin allow-scripts"
allowfullscreen="true"
webkitallowfullscreen="true"
mozallowfullscreen="true"
+17 -11
View File
@@ -147,21 +147,27 @@ export class HaPanelLogbook extends LitElement {
todayEnd.setDate(todayEnd.getDate() + 1);
todayEnd.setMilliseconds(todayEnd.getMilliseconds() - 1);
const yesterday = new Date(today);
yesterday.setDate(today.getDate() - 1);
const yesterdayEnd = new Date(today);
const todayCopy = new Date(today);
const yesterday = new Date(todayCopy.setDate(today.getDate() - 1));
const yesterdayEnd = new Date(yesterday);
yesterdayEnd.setDate(yesterdayEnd.getDate() + 1);
yesterdayEnd.setMilliseconds(yesterdayEnd.getMilliseconds() - 1);
const thisWeekStart = new Date(today);
thisWeekStart.setDate(today.getDate() - today.getDay());
const thisWeekEnd = new Date(thisWeekStart);
thisWeekEnd.setDate(thisWeekStart.getDate() + 7);
const thisWeekStart = new Date(
todayCopy.setDate(today.getDate() - today.getDay())
);
const thisWeekEnd = new Date(
todayCopy.setDate(thisWeekStart.getDate() + 7)
);
thisWeekEnd.setMilliseconds(thisWeekEnd.getMilliseconds() - 1);
const lastWeekStart = new Date(today);
lastWeekStart.setDate(today.getDate() - today.getDay() - 7);
const lastWeekEnd = new Date(lastWeekStart);
lastWeekEnd.setDate(lastWeekStart.getDate() + 7);
const lastWeekStart = new Date(
todayCopy.setDate(today.getDate() - today.getDay() - 7)
);
const lastWeekEnd = new Date(
todayCopy.setDate(lastWeekStart.getDate() + 7)
);
lastWeekEnd.setMilliseconds(lastWeekEnd.getMilliseconds() - 1);
this._ranges = {
+1 -3
View File
@@ -122,14 +122,12 @@ 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=${stateObj.state}
.value=${state}
.language=${this.hass!.language}
.label=${this._config!.unit ||
this.hass?.states[this._config!.entity].attributes
@@ -11,12 +11,9 @@ 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";
@@ -26,15 +23,12 @@ 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 {
@@ -55,14 +49,6 @@ 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;
}
@@ -115,15 +101,15 @@ class HuiShoppingListCard extends SubscribeMixin(LitElement)
})}
>
<div class="addRow">
<ha-svg-icon
<ha-icon
class="addButton"
.path=${mdiPlus}
icon="hass:plus"
.title=${this.hass!.localize(
"ui.panel.lovelace.cards.shopping-list.add_item"
)}
@click=${this._addItem}
>
</ha-svg-icon>
</ha-icon>
<paper-input
no-label-float
class="addBox"
@@ -132,27 +118,28 @@ 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>
${this._reordering
? html`
<div id="sortable">
${guard([this._uncheckedItems, this._renderEmptySortable], () =>
this._renderEmptySortable
? ""
: this._renderItems(this._uncheckedItems!)
)}
${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>
</div>
`
: this._renderItems(this._uncheckedItems!)}
)}
${this._checkedItems!.length > 0
? html`
<div class="divider"></div>
@@ -162,16 +149,16 @@ class HuiShoppingListCard extends SubscribeMixin(LitElement)
"ui.panel.lovelace.cards.shopping-list.checked_items"
)}
</span>
<ha-svg-icon
<ha-icon
class="clearall"
tabindex="0"
.path=${mdiNotificationClearAll}
icon="hass:notification-clear-all"
.title=${this.hass!.localize(
"ui.panel.lovelace.cards.shopping-list.clear_items"
)}
@click=${this._clearItems}
>
</ha-svg-icon>
</ha-icon>
</div>
${repeat(
this._checkedItems!,
@@ -200,44 +187,6 @@ 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;
@@ -299,54 +248,6 @@ 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 {
@@ -377,11 +278,6 @@ class HuiShoppingListCard extends SubscribeMixin(LitElement)
cursor: pointer;
}
.reorderButton {
padding-left: 16px;
cursor: pointer;
}
paper-checkbox {
padding-left: 4px;
padding-right: 20px;
+53 -68
View File
@@ -4,7 +4,6 @@ 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 {
@@ -12,7 +11,7 @@ declare global {
}
}
export const handleAction = async (
export const handleAction = (
node: HTMLElement,
hass: HomeAssistant,
config: {
@@ -23,7 +22,7 @@ export const handleAction = async (
double_tap_action?: ActionConfig;
},
action: string
): Promise<void> => {
): void => {
let actionConfig: ActionConfig | undefined;
if (action === "double_tap" && config.double_tap_action) {
@@ -40,78 +39,64 @@ export const handleAction = async (
};
}
const actionConfigs =
actionConfig.action === "multiple"
? Array.isArray(actionConfig.actions)
? actionConfig.actions
: [actionConfig.actions]
: [actionConfig];
if (
actionConfig.confirmation &&
(!actionConfig.confirmation.exemptions ||
!actionConfig.confirmation.exemptions.some(
(e) => e.user === hass!.user!.id
))
) {
forwardHaptic("warning");
for await (actionConfig of actionConfigs) {
if (
actionConfig.confirmation &&
(!actionConfig.confirmation.exemptions ||
!actionConfig.confirmation.exemptions.some(
(e) => e.user === hass!.user!.id
))
!confirm(
actionConfig.confirmation.text ||
`Are you sure you want to ${actionConfig.action}?`
)
) {
forwardHaptic("warning");
return;
}
}
if (
!(await showConfirmationDialog(node, {
text:
actionConfig.confirmation.text ||
hass.localize(
"ui.panel.lovelace.cards.action_confirmation",
"action",
actionConfig.action
),
}))
) {
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;
}
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);
}
case "fire-dom-event": {
fireEvent(node, "ll-custom", actionConfig);
}
}
};
@@ -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.join(".")}" is not supported by the UI editor.`
`Key "${failure.path[0]}" is not supported by the UI editor.`
);
} else {
errors.push(
@@ -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,16 +15,3 @@ 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);
@@ -130,15 +130,6 @@ 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>
`
: ""}
`;
}
@@ -164,8 +164,7 @@ class HuiGenericEntityRow extends LitElement {
}
.info {
margin-left: 16px;
margin-right: 8px;
flex: 1 0 30%;
flex: 1 0 60px;
}
.info,
.info > * {
@@ -182,6 +181,7 @@ class HuiGenericEntityRow extends LitElement {
}
.secondary,
ha-relative-time {
display: block;
color: var(--secondary-text-color);
}
state-badge {
@@ -57,9 +57,7 @@ class HuiTimestampDisplay extends LitElement {
}
if (isNaN(this.ts.getTime())) {
return html`${this.hass.localize(
"ui.panel.lovelace.components.timestamp-display.invalid"
)}`;
return html` Invalid date `;
}
const format = this._format;
@@ -70,9 +68,7 @@ class HuiTimestampDisplay extends LitElement {
if (format in FORMATS) {
return html` ${FORMATS[format](this.ts, this.hass.language)} `;
}
return html`${this.hass.localize(
"ui.panel.lovelace.components.timestamp-display.invalid_format"
)}`;
return html` Invalid format `;
}
protected updated(changedProperties: PropertyValues): void {
@@ -71,7 +71,7 @@ const DOMAIN_TO_ELEMENT_TYPE = {
switch: "toggle",
vacuum: "toggle",
// Temporary. Once climate is rewritten,
// water heater should get its own row.
// water heater should get it's own row.
water_heater: "climate",
input_datetime: "input-datetime",
weather: "weather",
@@ -28,6 +28,7 @@ const cardConfigStruct = object({
name: optional(string()),
states: optional(array()),
theme: optional(string()),
layout: optional(object()),
});
const includeDomains = ["alarm_control_panel"];
@@ -37,6 +37,7 @@ const cardConfigStruct = object({
hold_action: optional(actionConfigStruct),
theme: optional(string()),
show_state: optional(boolean()),
layout: optional(object()),
});
const actions = [
@@ -46,7 +47,6 @@ 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.optional"
"ui.panel.lovelace.editor.card.config.required"
)})"
.hass=${this.hass}
.value=${this._entity}
@@ -32,6 +32,7 @@ const cardConfigStruct = object({
initial_view: optional(string()),
theme: optional(string()),
entities: array(string()),
layout: optional(object()),
});
const views = ["dayGridMonth", "dayGridDay", "listWeek"];
@@ -36,6 +36,7 @@ const cardConfigStruct = object({
type: string(),
card: any(),
conditions: optional(array(conditionStruct)),
layout: optional(object()),
});
@customElement("hui-conditional-card-editor")
@@ -55,6 +55,7 @@ const cardConfigStruct = object({
entities: array(entitiesConfigStruct),
header: optional(headerFooterConfigStructs),
footer: optional(headerFooterConfigStructs),
layout: optional(object()),
});
@customElement("hui-entities-card-editor")
@@ -32,6 +32,7 @@ const cardConfigStruct = object({
unit: optional(string()),
theme: optional(string()),
footer: optional(headerFooterConfigStructs),
layout: optional(object()),
});
@customElement("hui-entity-card-editor")
@@ -31,6 +31,7 @@ const cardConfigStruct = object({
max: optional(number()),
severity: optional(object()),
theme: optional(string()),
layout: optional(object()),
});
const includeDomains = ["sensor"];
@@ -50,6 +50,7 @@ const cardConfigStruct = object({
show_icon: optional(boolean()),
state_color: optional(boolean()),
entities: array(entitiesConfigStruct),
layout: optional(object()),
});
@customElement("hui-glance-card-editor")
@@ -42,6 +42,7 @@ const cardConfigStruct = object({
title: optional(string()),
hours_to_show: optional(number()),
refresh_interval: optional(number()),
layout: optional(object()),
});
@customElement("hui-history-graph-card-editor")
@@ -23,6 +23,7 @@ const cardConfigStruct = object({
entity: string(),
name: optional(string()),
theme: optional(string()),
layout: optional(object()),
});
const includeDomains = ["humidifier"];
@@ -21,6 +21,7 @@ const cardConfigStruct = object({
title: optional(string()),
url: optional(string()),
aspect_ratio: optional(string()),
layout: optional(object()),
});
@customElement("hui-iframe-card-editor")
@@ -30,6 +30,7 @@ const cardConfigStruct = object({
icon: optional(string()),
hold_action: optional(actionConfigStruct),
double_tap_action: optional(actionConfigStruct),
layout: optional(object()),
});
const includeDomains = ["light"];
@@ -82,7 +83,6 @@ export class HuiLightCardEditor extends LitElement
"url",
"call-service",
"none",
"multiple",
];
return html`
@@ -26,6 +26,7 @@ const cardConfigStruct = object({
title: optional(string()),
hours_to_show: optional(number()),
theme: optional(string()),
layout: optional(object()),
});
@customElement("hui-logbook-card-editor")
@@ -46,6 +46,7 @@ const cardConfigStruct = object({
entities: array(entitiesConfigStruct),
hours_to_show: optional(number()),
geo_location_sources: optional(array()),
layout: optional(object()),
});
@customElement("hui-map-card-editor")
@@ -23,6 +23,7 @@ const cardConfigStruct = object({
title: optional(string()),
content: string(),
theme: optional(string()),
layout: optional(object()),
});
@customElement("hui-markdown-card-editor")
@@ -1,22 +1,23 @@
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"];
@@ -25,6 +25,7 @@ const cardConfigStruct = object({
tap_action: optional(actionConfigStruct),
hold_action: optional(actionConfigStruct),
theme: optional(string()),
layout: optional(object()),
});
@customElement("hui-picture-card-editor")
@@ -60,7 +61,7 @@ export class HuiPictureCardEditor extends LitElement
return html``;
}
const actions = ["navigate", "url", "call-service", "none", "multiple"];
const actions = ["navigate", "url", "call-service", "none"];
return html`
<div class="card-config">
@@ -39,6 +39,7 @@ const cardConfigStruct = object({
show_name: optional(boolean()),
show_state: optional(boolean()),
theme: optional(string()),
layout: optional(object()),
});
const includeDomains = ["camera"];
@@ -104,14 +105,7 @@ export class HuiPictureEntityCardEditor extends LitElement
return html``;
}
const actions = [
"more-info",
"toggle",
"navigate",
"call-service",
"none",
"multiple",
];
const actions = ["more-info", "toggle", "navigate", "call-service", "none"];
const views = ["auto", "live"];
const dir = computeRTLDirection(this.hass!);
@@ -42,6 +42,7 @@ const cardConfigStruct = object({
hold_action: optional(actionConfigStruct),
entities: array(entitiesConfigStruct),
theme: optional(string()),
layout: optional(object()),
});
const includeDomains = ["camera"];
@@ -108,14 +109,7 @@ export class HuiPictureGlanceCardEditor extends LitElement
return html``;
}
const actions = [
"more-info",
"toggle",
"navigate",
"call-service",
"none",
"multiple",
];
const actions = ["more-info", "toggle", "navigate", "call-service", "none"];
const views = ["auto", "live"];
return html`
@@ -24,6 +24,7 @@ const cardConfigStruct = object({
entity: string(),
name: optional(string()),
theme: optional(string()),
layout: optional(object()),
});
const includeDomains = ["plant"];
@@ -35,6 +35,7 @@ const cardConfigStruct = object({
detail: optional(number()),
theme: optional(string()),
hours_to_show: optional(number()),
layout: optional(object()),
});
const includeDomains = ["sensor"];
@@ -4,11 +4,12 @@ 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";
@@ -16,12 +17,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")
@@ -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,6 +39,7 @@ const cardConfigStruct = object({
title: optional(string()),
square: optional(boolean()),
columns: optional(number()),
layout: optional(object()),
});
@customElement("hui-stack-card-editor")
@@ -23,6 +23,7 @@ const cardConfigStruct = object({
entity: string(),
name: optional(string()),
theme: optional(string()),
layout: optional(object()),
});
const includeDomains = ["climate"];
@@ -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,6 +28,7 @@ const cardConfigStruct = object({
theme: optional(string()),
show_forecast: optional(boolean()),
secondary_info_attribute: optional(string()),
layout: optional(object()),
});
const includeDomains = ["weather"];
-1
View File
@@ -98,7 +98,6 @@ export const actionConfigStruct = object({
url_path: optional(string()),
service: optional(string()),
service_data: optional(object()),
actions: optional(array()),
});
const buttonEntitiesRowConfigStruct = object({
@@ -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
>
`;
@@ -25,7 +25,7 @@ import { handleAction } from "../common/handle-action";
import { UNAVAILABLE_STATES } from "../../../data/entity";
interface SensorEntityConfig extends EntitiesCardEntityConfig {
format?: "relative" | "total" | "date" | "time" | "datetime";
format?: "relative" | "date" | "time" | "datetime";
}
@customElement("hui-sensor-entity-row")
+2 -2
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.stay"),
confirmText: this.hass!.localize("ui.common.leave"),
dismissText: this.hass!.localize("ui.common.leave"),
confirmText: this.hass!.localize("ui.common.stay"),
}))
) {
return;
+1 -2
View File
@@ -33,8 +33,7 @@ export default <T extends Constructor<HassBaseEl>>(superClass: T) =>
duration: 0,
dismissable: false,
action: {
text:
this.hass!.localize("ui.notification_toast.dismiss") || "Dismiss",
text: this.hass!.localize("ui.notification_toast.dismiss"),
action: () => {},
},
});

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