-
-
-
+
`;
}
@@ -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);
diff --git a/gallery/src/data/plants.ts b/gallery/src/data/plants.ts
new file mode 100644
index 0000000000..362afaac78
--- /dev/null
+++ b/gallery/src/data/plants.ts
@@ -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",
+ }),
+];
diff --git a/gallery/src/demos/demo-hui-alarm-panel-card.ts b/gallery/src/demos/demo-hui-alarm-panel-card.ts
index efeacf5f19..4c66b22559 100644
--- a/gallery/src/demos/demo-hui-alarm-panel-card.ts
+++ b/gallery/src/demos/demo-hui-alarm-panel-card.ts
@@ -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 {
+ customElement,
+ html,
+ LitElement,
+ 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`
`;
+@customElement("demo-hui-alarm-panel-card")
+class DemoAlarmPanelEntity extends LitElement {
+ @query("#demos") private _demoRoot!: HTMLElement;
+
+ protected render(): TemplateResult {
+ return html`
`;
}
- 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);
}
}
diff --git a/gallery/src/demos/demo-hui-conditional-card.ts b/gallery/src/demos/demo-hui-conditional-card.ts
index 561ad45376..cf70db64aa 100644
--- a/gallery/src/demos/demo-hui-conditional-card.ts
+++ b/gallery/src/demos/demo-hui-conditional-card.ts
@@ -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 {
+ customElement,
+ html,
+ LitElement,
+ 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`
`;
+@customElement("demo-hui-conditional-card")
+class DemoConditional extends LitElement {
+ @query("#demos") private _demoRoot!: HTMLElement;
+
+ protected render(): TemplateResult {
+ return html`
`;
}
- 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);
}
}
diff --git a/gallery/src/demos/demo-hui-entities-card.ts b/gallery/src/demos/demo-hui-entities-card.ts
index bb0261e9d5..7511e061b5 100644
--- a/gallery/src/demos/demo-hui-entities-card.ts
+++ b/gallery/src/demos/demo-hui-entities-card.ts
@@ -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 {
+ customElement,
+ html,
+ LitElement,
+ 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`
`;
+@customElement("demo-hui-entities-card")
+class DemoEntities extends LitElement {
+ @query("#demos") private _demoRoot!: HTMLElement;
+
+ protected render(): TemplateResult {
+ return html`
`;
}
- 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);
}
}
diff --git a/gallery/src/demos/demo-hui-entity-button-card.ts b/gallery/src/demos/demo-hui-entity-button-card.ts
index 5c9430bf64..1e3201fa10 100644
--- a/gallery/src/demos/demo-hui-entity-button-card.ts
+++ b/gallery/src/demos/demo-hui-entity-button-card.ts
@@ -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 {
+ customElement,
+ html,
+ LitElement,
+ 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";
@@ -48,7 +53,7 @@ const CONFIGS = [
config: `
- type: button
entity: light.bed_light
- tap_action:
+ tap_action:
action: toggle
`,
},
@@ -69,24 +74,19 @@ const CONFIGS = [
},
];
-class DemoButtonEntity extends PolymerElement {
- static get template() {
- return html`
`;
+@customElement("demo-hui-entity-button-card")
+class DemoButtonEntity extends LitElement {
+ @query("#demos") private _demoRoot!: HTMLElement;
+
+ protected render(): TemplateResult {
+ return html`
`;
}
- 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);
}
}
diff --git a/gallery/src/demos/demo-hui-entity-filter-card.ts b/gallery/src/demos/demo-hui-entity-filter-card.ts
index 66a0c08b70..c3e22f0e46 100644
--- a/gallery/src/demos/demo-hui-entity-filter-card.ts
+++ b/gallery/src/demos/demo-hui-entity-filter-card.ts
@@ -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 {
+ customElement,
+ html,
+ LitElement,
+ 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";
@@ -43,7 +48,7 @@ const ENTITIES = [
const CONFIGS = [
{
- heading: "Controller",
+ heading: "Unfiltered controller",
config: `
- type: entities
entities:
@@ -53,7 +58,7 @@ const CONFIGS = [
`,
},
{
- heading: "Basic",
+ heading: "Filtered entities card",
config: `
- type: entity-filter
entities:
@@ -69,7 +74,27 @@ const CONFIGS = [
`,
},
{
- heading: "With card config",
+ heading: 'With "entities" card config',
+ config: `
+- type: entity-filter
+ entities:
+ - device_tracker.demo_anne_therese
+ - device_tracker.demo_home_boy
+ - device_tracker.demo_paulus
+ - light.bed_light
+ - light.ceiling_lights
+ - light.kitchen_lights
+ state_filter:
+ - "on"
+ - not_home
+ card:
+ type: entities
+ title: Custom Title
+ show_header_toggle: false
+ `,
+ },
+ {
+ heading: 'With "glance" card config',
config: `
- type: entity-filter
entities:
@@ -84,31 +109,27 @@ const CONFIGS = [
- not_home
card:
type: glance
- show_state: false
+ show_state: true
+ title: Custom Title
`,
},
];
-class DemoFilter extends PolymerElement {
- static get template() {
- return html`
`;
+@customElement("demo-hui-entity-filter-card")
+class DemoEntityFilter extends LitElement {
+ @query("#demos") private _demoRoot!: HTMLElement;
+
+ protected render(): TemplateResult {
+ return html`
`;
}
- 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);
diff --git a/gallery/src/demos/demo-hui-gauge-card.ts b/gallery/src/demos/demo-hui-gauge-card.ts
index 1704d31f6e..35e794f317 100644
--- a/gallery/src/demos/demo-hui-gauge-card.ts
+++ b/gallery/src/demos/demo-hui-gauge-card.ts
@@ -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 {
+ customElement,
+ html,
+ LitElement,
+ 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`
`;
+@customElement("demo-hui-gauge-card")
+class DemoGaugeEntity extends LitElement {
+ @query("#demos") private _demoRoot!: HTMLElement;
+
+ protected render(): TemplateResult {
+ return html`
`;
}
- 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);
}
}
diff --git a/gallery/src/demos/demo-hui-glance-card.ts b/gallery/src/demos/demo-hui-glance-card.ts
index 5455232a3d..2d3d83193e 100644
--- a/gallery/src/demos/demo-hui-glance-card.ts
+++ b/gallery/src/demos/demo-hui-glance-card.ts
@@ -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 {
+ customElement,
+ html,
+ LitElement,
+ 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";
@@ -77,7 +82,8 @@ const CONFIGS = [
heading: "With title",
config: `
- type: glance
- title: This is glance
+ title: Custom title
+ columns: 4
entities:
- device_tracker.demo_paulus
- media_player.living_room
@@ -104,9 +110,10 @@ const CONFIGS = [
`,
},
{
- heading: "No name",
+ heading: "No entity names",
config: `
- type: glance
+ columns: 4
show_name: false
entities:
- device_tracker.demo_paulus
@@ -119,9 +126,10 @@ const CONFIGS = [
`,
},
{
- heading: "No state",
+ heading: "No state labels",
config: `
- type: glance
+ columns: 4
show_state: false
entities:
- device_tracker.demo_paulus
@@ -134,9 +142,10 @@ const CONFIGS = [
`,
},
{
- heading: "No name and no state",
+ heading: "No names and no state labels",
config: `
- type: glance
+ columns: 4
show_name: false
show_state: false
entities:
@@ -150,47 +159,24 @@ const CONFIGS = [
`,
},
{
- heading: "Custom name, custom icon",
+ heading: "Custom name + custom icon",
config: `
- type: glance
+ columns: 4
entities:
- entity: device_tracker.demo_paulus
name: ¯\\_(ツ)_/¯
icon: mdi:home-assistant
- - media_player.living_room
- - sun.sun
- - cover.kitchen_window
- - entity: light.kitchen_lights
- icon: mdi:alarm-light
- - lock.kitchen_door
- - light.ceiling_lights
- `,
- },
- {
- heading: "Custom tap action",
- config: `
-- type: glance
- entities:
- - entity: lock.kitchen_door
- tap_action:
- type: toggle
- - entity: light.ceiling_lights
- tap_action:
- action: call-service
- service: light.turn_on
- service_data:
- entity_id: light.ceiling_lights
- - device_tracker.demo_paulus
- - media_player.living_room
- - sun.sun
- - cover.kitchen_window
- - light.kitchen_lights
+ - entity: media_player.living_room
+ name: ¯\\_(ツ)_/¯
+ icon: mdi:home-assistant
`,
},
{
heading: "Selectively hidden name",
config: `
- type: glance
+ columns: 4
entities:
- device_tracker.demo_paulus
- entity: media_player.living_room
@@ -199,45 +185,51 @@ const CONFIGS = [
- entity: cover.kitchen_window
name:
- light.kitchen_lights
+ - entity: lock.kitchen_door
+ name:
+ - light.ceiling_lights
`,
},
{
- heading: "Primary theme",
+ heading: "Custom tap action",
config: `
- type: glance
- theming: primary
+ columns: 4
entities:
- - device_tracker.demo_paulus
- - media_player.living_room
- - sun.sun
- - cover.kitchen_window
- - light.kitchen_lights
- - lock.kitchen_door
- - light.ceiling_lights
+ - entity: lock.kitchen_door
+ name: Custom
+ tap_action:
+ type: toggle
+ - entity: light.ceiling_lights
+ name: Custom
+ tap_action:
+ action: call-service
+ service: light.turn_on
+ service_data:
+ entity_id: light.ceiling_lights
+ - entity: sun.sun
+ name: Regular
+ - entity: light.kitchen_lights
+ name: Regular
`,
},
];
-class DemoPicEntity extends PolymerElement {
- static get template() {
- return html`
`;
+@customElement("demo-hui-glance-card")
+class DemoGlanceEntity extends LitElement {
+ @query("#demos") private _demoRoot!: HTMLElement;
+
+ protected render(): TemplateResult {
+ return html`
`;
}
- 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);
diff --git a/gallery/src/demos/demo-hui-iframe-card.ts b/gallery/src/demos/demo-hui-iframe-card.ts
index 596f6d302f..a2d493476b 100644
--- a/gallery/src/demos/demo-hui-iframe-card.ts
+++ b/gallery/src/demos/demo-hui-iframe-card.ts
@@ -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 { customElement, html, LitElement, 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`
`;
- }
-
- static get properties() {
- return {
- _configs: {
- type: Object,
- value: CONFIGS,
- },
- };
+@customElement("demo-hui-iframe-card")
+class DemoIframe extends LitElement {
+ protected render(): TemplateResult {
+ return html`
`;
}
}
diff --git a/gallery/src/demos/demo-hui-light-card.ts b/gallery/src/demos/demo-hui-light-card.ts
index 97808f930a..71d12b4af8 100644
--- a/gallery/src/demos/demo-hui-light-card.ts
+++ b/gallery/src/demos/demo-hui-light-card.ts
@@ -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 {
+ customElement,
+ html,
+ LitElement,
+ 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`
`;
+@customElement("demo-hui-light-card")
+class DemoLightEntity extends LitElement {
+ @query("#demos") private _demoRoot!: HTMLElement;
+
+ protected render(): TemplateResult {
+ return html`
`;
}
- 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);
}
}
diff --git a/gallery/src/demos/demo-hui-map-card.ts b/gallery/src/demos/demo-hui-map-card.ts
index 392826fb93..2cc55f499d 100644
--- a/gallery/src/demos/demo-hui-map-card.ts
+++ b/gallery/src/demos/demo-hui-map-card.ts
@@ -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 {
+ customElement,
+ html,
+ LitElement,
+ 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`
`;
+@customElement("demo-hui-map-card")
+class DemoMap extends LitElement {
+ @query("#demos") private _demoRoot!: HTMLElement;
+
+ protected render(): TemplateResult {
+ return html`
`;
}
- 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);
}
}
diff --git a/gallery/src/demos/demo-hui-markdown-card.ts b/gallery/src/demos/demo-hui-markdown-card.ts
index 1ae6b5f0f7..8321eff7d3 100644
--- a/gallery/src/demos/demo-hui-markdown-card.ts
+++ b/gallery/src/demos/demo-hui-markdown-card.ts
@@ -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 {
+ customElement,
+ html,
+ LitElement,
+ 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`
`;
+@customElement("demo-hui-markdown-card")
+class DemoMarkdown extends LitElement {
+ @query("#demos") private _demoRoot!: HTMLElement;
+
+ protected render(): TemplateResult {
+ return html`
`;
}
- 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);
}
}
diff --git a/gallery/src/demos/demo-hui-media-control-card.ts b/gallery/src/demos/demo-hui-media-control-card.ts
index 1f12c851bc..55bbc0168c 100644
--- a/gallery/src/demos/demo-hui-media-control-card.ts
+++ b/gallery/src/demos/demo-hui-media-control-card.ts
@@ -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 {
+ customElement,
+ html,
+ LitElement,
+ 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`
`;
+@customElement("demo-hui-media-control-card")
+class DemoHuiMediaControlCard extends LitElement {
+ @query("#demos") private _demoRoot!: HTMLElement;
+
+ protected render(): TemplateResult {
+ return html`
`;
}
- 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);
diff --git a/gallery/src/demos/demo-hui-media-player-rows.ts b/gallery/src/demos/demo-hui-media-player-rows.ts
index 147f36985e..9ffabfee9b 100644
--- a/gallery/src/demos/demo-hui-media-player-rows.ts
+++ b/gallery/src/demos/demo-hui-media-player-rows.ts
@@ -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 {
+ customElement,
+ html,
+ LitElement,
+ PropertyValues,
+ query,
+ TemplateResult,
+} from "lit-element";
import { provideHass } from "../../../src/fake_data/provide_hass";
import "../components/demo-cards";
import { createMediaPlayerEntities } from "../data/media_players";
@@ -26,9 +31,9 @@ const CONFIGS = [
- entity: media_player.android_cast
name: Screen casting
- entity: media_player.image_display
- name: Digital Picture Frame
+ name: Digital Picture Frame
- entity: media_player.sonos_idle
- name: Sonos Idle
+ name: Sonos Idle
- entity: media_player.idle_browse_media
name: Idle waiting for Browse Media
- entity: media_player.theater_off
@@ -38,7 +43,7 @@ const CONFIGS = [
- entity: media_player.theater_off_static
name: Player Off (cannot be switched on)
- entity: media_player.theater_on_static
- name: Player On (cannot be switched off)
+ name: Player On (cannot be switched off)
- entity: media_player.idle
name: Player Idle
- entity: media_player.playing
@@ -55,26 +60,21 @@ const CONFIGS = [
},
];
-class DemoHuiMediaPlayerRows extends PolymerElement {
- static get template() {
- return html`
`;
+@customElement("demo-hui-media-player-row")
+class DemoHuiMediaPlayerRow extends LitElement {
+ @query("#demos") private _demoRoot!: HTMLElement;
+
+ protected render(): TemplateResult {
+ return html`
`;
}
- 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);
diff --git a/gallery/src/demos/demo-hui-picture-elements-card.ts b/gallery/src/demos/demo-hui-picture-elements-card.ts
index 6e0ec3360a..6e6ffd86a0 100644
--- a/gallery/src/demos/demo-hui-picture-elements-card.ts
+++ b/gallery/src/demos/demo-hui-picture-elements-card.ts
@@ -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 {
+ customElement,
+ html,
+ LitElement,
+ 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`
`;
+@customElement("demo-hui-picture-elements-card")
+class DemoPictureElements extends LitElement {
+ @query("#demos") private _demoRoot!: HTMLElement;
+
+ protected render(): TemplateResult {
+ return html`
`;
}
- 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);
diff --git a/gallery/src/demos/demo-hui-picture-entity-card.ts b/gallery/src/demos/demo-hui-picture-entity-card.ts
index d4df8f31f4..fd0c5fea94 100644
--- a/gallery/src/demos/demo-hui-picture-entity-card.ts
+++ b/gallery/src/demos/demo-hui-picture-entity-card.ts
@@ -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 {
+ customElement,
+ html,
+ LitElement,
+ 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`
`;
+@customElement("demo-hui-picture-entity-card")
+class DemoPictureEntity extends LitElement {
+ @query("#demos") private _demoRoot!: HTMLElement;
+
+ protected render(): TemplateResult {
+ return html`
`;
}
- 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);
diff --git a/gallery/src/demos/demo-hui-picture-glance-card.ts b/gallery/src/demos/demo-hui-picture-glance-card.ts
index 684aaa2071..9ecafb7f5a 100644
--- a/gallery/src/demos/demo-hui-picture-glance-card.ts
+++ b/gallery/src/demos/demo-hui-picture-glance-card.ts
@@ -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 {
+ customElement,
+ html,
+ LitElement,
+ 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`
`;
+@customElement("demo-hui-picture-glance-card")
+class DemoPictureGlance extends LitElement {
+ @query("#demos") private _demoRoot!: HTMLElement;
+
+ protected render(): TemplateResult {
+ return html`
`;
}
- 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);
diff --git a/gallery/src/demos/demo-hui-plant-card.ts b/gallery/src/demos/demo-hui-plant-card.ts
new file mode 100644
index 0000000000..a1d49afe5b
--- /dev/null
+++ b/gallery/src/demos/demo-hui-plant-card.ts
@@ -0,0 +1,55 @@
+import {
+ customElement,
+ html,
+ LitElement,
+ 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`
`;
+ }
+
+ 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);
diff --git a/gallery/src/demos/demo-hui-shopping-list-card.ts b/gallery/src/demos/demo-hui-shopping-list-card.ts
index 9abb1afc1e..a17bd61f12 100644
--- a/gallery/src/demos/demo-hui-shopping-list-card.ts
+++ b/gallery/src/demos/demo-hui-shopping-list-card.ts
@@ -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 {
+ customElement,
+ html,
+ LitElement,
+ 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`
`;
+@customElement("demo-hui-shopping-list-card")
+class DemoShoppingListEntity extends LitElement {
+ @query("#demos") private _demoRoot!: HTMLElement;
+
+ protected render(): TemplateResult {
+ return html`
`;
}
- 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 },
diff --git a/gallery/src/demos/demo-hui-stack-card.ts b/gallery/src/demos/demo-hui-stack-card.ts
index 982eab4f25..26f03c4051 100644
--- a/gallery/src/demos/demo-hui-stack-card.ts
+++ b/gallery/src/demos/demo-hui-stack-card.ts
@@ -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 {
+ customElement,
+ html,
+ LitElement,
+ 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`
`;
+@customElement("demo-hui-stack-card")
+class DemoStack extends LitElement {
+ @query("#demos") private _demoRoot!: HTMLElement;
+
+ protected render(): TemplateResult {
+ return html`
`;
}
- 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);
}
diff --git a/gallery/src/demos/demo-hui-thermostat-card.ts b/gallery/src/demos/demo-hui-thermostat-card.ts
index 81b344e689..30e08d3e29 100644
--- a/gallery/src/demos/demo-hui-thermostat-card.ts
+++ b/gallery/src/demos/demo-hui-thermostat-card.ts
@@ -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 {
+ customElement,
+ html,
+ LitElement,
+ 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`
`;
+@customElement("demo-hui-thermostat-card")
+class DemoThermostatEntity extends LitElement {
+ @query("#demos") private _demoRoot!: HTMLElement;
+
+ protected render(): TemplateResult {
+ return html`
`;
}
- 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);
}
}
diff --git a/gallery/src/demos/demo-more-info-light.ts b/gallery/src/demos/demo-more-info-light.ts
index 70b77560d9..3a05f1becf 100644
--- a/gallery/src/demos/demo-more-info-light.ts
+++ b/gallery/src/demos/demo-more-info-light.ts
@@ -1,12 +1,29 @@
-import { html } from "@polymer/polymer/lib/utils/html-tag";
-/* eslint-plugin-disable lit */
-import { PolymerElement } from "@polymer/polymer/polymer-element";
+import {
+ customElement,
+ html,
+ LitElement,
+ property,
+ PropertyValues,
+ query,
+ TemplateResult,
+} from "lit-element";
import "../../../src/components/ha-card";
-import { SUPPORT_BRIGHTNESS } from "../../../src/data/light";
-import { getEntity } from "../../../src/fake_data/entity";
-import { provideHass } from "../../../src/fake_data/provide_hass";
-import "../components/demo-more-infos";
+import {
+ SUPPORT_BRIGHTNESS,
+ SUPPORT_COLOR,
+ SUPPORT_COLOR_TEMP,
+ SUPPORT_EFFECT,
+ SUPPORT_FLASH,
+ SUPPORT_TRANSITION,
+ SUPPORT_WHITE_VALUE,
+} from "../../../src/data/light";
import "../../../src/dialogs/more-info/more-info-content";
+import { getEntity } from "../../../src/fake_data/entity";
+import {
+ MockHomeAssistant,
+ provideHass,
+} from "../../../src/fake_data/provide_hass";
+import "../components/demo-more-infos";
const ENTITIES = [
getEntity("light", "bed_light", "on", {
@@ -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`
ent.entityId)}
>
`;
}
- 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);
}
}
diff --git a/gallery/src/demos/demo-util-long-press.ts b/gallery/src/demos/demo-util-long-press.ts
index 518af1df39..9fa774918f 100644
--- a/gallery/src/demos/demo-util-long-press.ts
+++ b/gallery/src/demos/demo-util-long-press.ts
@@ -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 {
-
(try pressing and scrolling too!)
+
Try pressing and scrolling too!
`
)}
@@ -62,5 +63,3 @@ export class DemoUtilLongPress extends LitElement {
`;
}
}
-
-customElements.define("demo-util-long-press", DemoUtilLongPress);
diff --git a/gallery/src/ha-gallery.js b/gallery/src/ha-gallery.js
index 779790b632..c1de40203d 100644
--- a/gallery/src/ha-gallery.js
+++ b/gallery/src/ha-gallery.js
@@ -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`
diff --git a/hassio/src/addon-store/hassio-addon-store.ts b/hassio/src/addon-store/hassio-addon-store.ts
index fc15fc38dc..2e21b19f84 100644
--- a/hassio/src/addon-store/hassio-addon-store.ts
+++ b/hassio/src/addon-store/hassio-addon-store.ts
@@ -12,6 +12,7 @@ import {
} from "lit-element";
import { html, TemplateResult } from "lit-html";
import { atLeastVersion } from "../../../src/common/config/version";
+import { fireEvent } from "../../../src/common/dom/fire_event";
import "../../../src/common/search/search-input";
import "../../../src/components/ha-button-menu";
import "../../../src/components/ha-svg-icon";
@@ -22,6 +23,7 @@ import {
reloadHassioAddons,
} from "../../../src/data/hassio/addon";
import { extractApiErrorMessage } from "../../../src/data/hassio/common";
+import { fetchHassioSupervisorInfo } from "../../../src/data/hassio/supervisor";
import "../../../src/layouts/hass-loading-screen";
import "../../../src/layouts/hass-tabs-subpage";
import { HomeAssistant, Route } from "../../../src/types";
@@ -190,7 +192,11 @@ class HassioAddonStore extends LitElement {
private async _loadData() {
try {
- const addonsInfo = await fetchHassioAddonsInfo(this.hass);
+ const [addonsInfo, supervisor] = await Promise.all([
+ fetchHassioAddonsInfo(this.hass),
+ fetchHassioSupervisorInfo(this.hass),
+ ]);
+ fireEvent(this, "supervisor-update", { supervisor });
this._repos = addonsInfo.repositories;
this._repos.sort(sortRepos);
this._addons = addonsInfo.addons;
diff --git a/hassio/src/addon-view/config/hassio-addon-audio.ts b/hassio/src/addon-view/config/hassio-addon-audio.ts
index 71ef1170d5..e3349b0144 100644
--- a/hassio/src/addon-view/config/hassio-addon-audio.ts
+++ b/hassio/src/addon-view/config/hassio-addon-audio.ts
@@ -7,13 +7,14 @@ import {
CSSResult,
customElement,
html,
+ internalProperty,
LitElement,
property,
- internalProperty,
PropertyValues,
TemplateResult,
} from "lit-element";
import "web-animations-js/web-animations-next-lite.min";
+import "../../../../src/components/buttons/ha-progress-button";
import "../../../../src/components/ha-card";
import {
HassioAddonDetails,
@@ -28,7 +29,6 @@ import { haStyle } from "../../../../src/resources/styles";
import { HomeAssistant } from "../../../../src/types";
import { suggestAddonRestart } from "../../dialogs/suggestAddonRestart";
import { hassioStyle } from "../../resources/hassio-style";
-import "../../../../src/components/buttons/ha-progress-button";
@customElement("hassio-addon-audio")
class HassioAddonAudio extends LitElement {
diff --git a/hassio/src/addon-view/config/hassio-addon-config-tab.ts b/hassio/src/addon-view/config/hassio-addon-config-tab.ts
index 29e3f1778e..323d50df73 100644
--- a/hassio/src/addon-view/config/hassio-addon-config-tab.ts
+++ b/hassio/src/addon-view/config/hassio-addon-config-tab.ts
@@ -7,11 +7,11 @@ import {
property,
TemplateResult,
} from "lit-element";
+import "../../../../src/components/ha-circular-progress";
import { HassioAddonDetails } from "../../../../src/data/hassio/addon";
import { haStyle } from "../../../../src/resources/styles";
import { HomeAssistant } from "../../../../src/types";
import { hassioStyle } from "../../resources/hassio-style";
-import "../../../../src/components/ha-circular-progress";
import "./hassio-addon-audio";
import "./hassio-addon-config";
import "./hassio-addon-network";
@@ -26,28 +26,41 @@ class HassioAddonConfigDashboard extends LitElement {
if (!this.addon) {
return html`
`;
}
+ const hasOptions =
+ this.addon.options && Object.keys(this.addon.options).length;
+ const hasSchema =
+ this.addon.schema && Object.keys(this.addon.schema).length;
+
return html`
-
- ${this.addon.network
+ ${hasOptions || hasSchema || this.addon.network || this.addon.audio
? html`
-
+ ${hasOptions || hasSchema
+ ? html`
+
+ `
+ : ""}
+ ${this.addon.network
+ ? html`
+
+ `
+ : ""}
+ ${this.addon.audio
+ ? html`
+
+ `
+ : ""}
`
- : ""}
- ${this.addon.audio
- ? html`
-
- `
- : ""}
+ : "This add-on does not expose configuration for you to mess with.... 👋"}
`;
}
diff --git a/hassio/src/addon-view/hassio-addon-dashboard.ts b/hassio/src/addon-view/hassio-addon-dashboard.ts
index 9876486d5f..bee5d12166 100644
--- a/hassio/src/addon-view/hassio-addon-dashboard.ts
+++ b/hassio/src/addon-view/hassio-addon-dashboard.ts
@@ -14,12 +14,12 @@ import {
TemplateResult,
} from "lit-element";
import memoizeOne from "memoize-one";
+import "../../../src/components/ha-circular-progress";
import {
fetchHassioAddonInfo,
HassioAddonDetails,
} from "../../../src/data/hassio/addon";
import "../../../src/layouts/hass-tabs-subpage";
-import "../../../src/components/ha-circular-progress";
import type { PageNavigation } from "../../../src/layouts/hass-tabs-subpage";
import { haStyle } from "../../../src/resources/styles";
import { HomeAssistant, Route } from "../../../src/types";
diff --git a/hassio/src/addon-view/info/hassio-addon-info-tab.ts b/hassio/src/addon-view/info/hassio-addon-info-tab.ts
index a2c8fc378b..620ea89f51 100644
--- a/hassio/src/addon-view/info/hassio-addon-info-tab.ts
+++ b/hassio/src/addon-view/info/hassio-addon-info-tab.ts
@@ -7,8 +7,8 @@ import {
property,
TemplateResult,
} from "lit-element";
-import { HassioAddonDetails } from "../../../../src/data/hassio/addon";
import "../../../../src/components/ha-circular-progress";
+import { HassioAddonDetails } from "../../../../src/data/hassio/addon";
import { haStyle } from "../../../../src/resources/styles";
import { HomeAssistant } from "../../../../src/types";
import { hassioStyle } from "../../resources/hassio-style";
diff --git a/hassio/src/addon-view/log/hassio-addon-log-tab.ts b/hassio/src/addon-view/log/hassio-addon-log-tab.ts
index 8961e61dd9..54bace246b 100644
--- a/hassio/src/addon-view/log/hassio-addon-log-tab.ts
+++ b/hassio/src/addon-view/log/hassio-addon-log-tab.ts
@@ -7,8 +7,8 @@ import {
property,
TemplateResult,
} from "lit-element";
-import { HassioAddonDetails } from "../../../../src/data/hassio/addon";
import "../../../../src/components/ha-circular-progress";
+import { HassioAddonDetails } from "../../../../src/data/hassio/addon";
import { haStyle } from "../../../../src/resources/styles";
import { HomeAssistant } from "../../../../src/types";
import { hassioStyle } from "../../resources/hassio-style";
diff --git a/hassio/src/components/supervisor-metric.ts b/hassio/src/components/supervisor-metric.ts
new file mode 100644
index 0000000000..b0af0fd9a2
--- /dev/null
+++ b/hassio/src/components/supervisor-metric.ts
@@ -0,0 +1,87 @@
+import {
+ css,
+ CSSResult,
+ customElement,
+ html,
+ LitElement,
+ property,
+ TemplateResult,
+} from "lit-element";
+import { classMap } from "lit-html/directives/class-map";
+import "../../../src/components/ha-bar";
+import "../../../src/components/ha-settings-row";
+import { roundWithOneDecimal } from "../../../src/util/calculate";
+
+@customElement("supervisor-metric")
+class SupervisorMetric extends LitElement {
+ @property({ type: Number }) public value!: number;
+
+ @property({ type: String }) public description!: string;
+
+ @property({ type: String }) public tooltip?: string;
+
+ protected render(): TemplateResult {
+ const roundedValue = roundWithOneDecimal(this.value);
+ return html`
+
+ ${this.description}
+
+
+
+ ${roundedValue}%
+
+ 50,
+ "target-critical": roundedValue > 85,
+ })}"
+ .value=${this.value}
+ >
+
+ `;
+ }
+
+ static get styles(): CSSResult {
+ return css`
+ ha-settings-row {
+ padding: 0;
+ height: 54px;
+ width: 100%;
+ }
+ ha-settings-row > div[slot="description"] {
+ white-space: normal;
+ color: var(--secondary-text-color);
+ display: flex;
+ justify-content: space-between;
+ }
+ ha-bar {
+ --ha-bar-primary-color: var(
+ --hassio-bar-ok-color,
+ var(--success-color)
+ );
+ }
+ .target-warning {
+ --ha-bar-primary-color: var(
+ --hassio-bar-warning-color,
+ var(--warning-color)
+ );
+ }
+ .target-critical {
+ --ha-bar-primary-color: var(
+ --hassio-bar-critical-color,
+ var(--error-color)
+ );
+ }
+ .value {
+ width: 42px;
+ padding-right: 4px;
+ }
+ `;
+ }
+}
+
+declare global {
+ interface HTMLElementTagNameMap {
+ "supervisor-metric": SupervisorMetric;
+ }
+}
diff --git a/hassio/src/dialogs/markdown/dialog-hassio-markdown.ts b/hassio/src/dialogs/markdown/dialog-hassio-markdown.ts
index 6b36d31e5c..95177a18f5 100644
--- a/hassio/src/dialogs/markdown/dialog-hassio-markdown.ts
+++ b/hassio/src/dialogs/markdown/dialog-hassio-markdown.ts
@@ -3,9 +3,9 @@ import {
CSSResult,
customElement,
html,
+ internalProperty,
LitElement,
property,
- internalProperty,
TemplateResult,
} from "lit-element";
import { createCloseHeading } from "../../../../src/components/ha-dialog";
diff --git a/hassio/src/entrypoint.ts b/hassio/src/entrypoint.ts
index 00c34787e6..24d3e45359 100644
--- a/hassio/src/entrypoint.ts
+++ b/hassio/src/entrypoint.ts
@@ -1,6 +1,7 @@
+// Compat needs to be first import
import "../../src/resources/compatibility";
-import "../../src/resources/safari-14-attachshadow-patch";
import "../../src/resources/roboto";
+import "../../src/resources/safari-14-attachshadow-patch";
import "./hassio-main";
const styleEl = document.createElement("style");
diff --git a/hassio/src/hassio-main.ts b/hassio/src/hassio-main.ts
index 2ee909885e..81d736e881 100644
--- a/hassio/src/hassio-main.ts
+++ b/hassio/src/hassio-main.ts
@@ -1,11 +1,11 @@
-import { html, PropertyValues, customElement, property } from "lit-element";
-import "./hassio-router";
-import { HomeAssistant, Route } from "../../src/types";
-import { HassioPanelInfo } from "../../src/data/hassio/supervisor";
+import { customElement, html, property, PropertyValues } from "lit-element";
+import { atLeastVersion } from "../../src/common/config/version";
import { applyThemesOnElement } from "../../src/common/dom/apply_themes_on_element";
import { fireEvent } from "../../src/common/dom/fire_event";
+import { HassioPanelInfo } from "../../src/data/hassio/supervisor";
import { makeDialogManager } from "../../src/dialogs/make-dialog-manager";
-import { atLeastVersion } from "../../src/common/config/version";
+import { HomeAssistant, Route } from "../../src/types";
+import "./hassio-router";
import { SupervisorBaseElement } from "./supervisor-base-element";
@customElement("hassio-main")
diff --git a/hassio/src/ingress-view/hassio-ingress-view.ts b/hassio/src/ingress-view/hassio-ingress-view.ts
index 14962c6f36..531673ac2f 100644
--- a/hassio/src/ingress-view/hassio-ingress-view.ts
+++ b/hassio/src/ingress-view/hassio-ingress-view.ts
@@ -1,14 +1,17 @@
+import { mdiMenu } from "@mdi/js";
import {
css,
CSSResult,
customElement,
html,
+ internalProperty,
LitElement,
property,
- internalProperty,
PropertyValues,
TemplateResult,
} from "lit-element";
+import { fireEvent } from "../../../src/common/dom/fire_event";
+import { navigate } from "../../../src/common/navigate";
import {
fetchHassioAddonInfo,
HassioAddonDetails,
@@ -17,13 +20,10 @@ import {
createHassioSession,
validateHassioSession,
} from "../../../src/data/hassio/ingress";
+import { showAlertDialog } from "../../../src/dialogs/generic/show-dialog-box";
import "../../../src/layouts/hass-loading-screen";
import "../../../src/layouts/hass-subpage";
import { HomeAssistant, Route } from "../../../src/types";
-import { showAlertDialog } from "../../../src/dialogs/generic/show-dialog-box";
-import { navigate } from "../../../src/common/navigate";
-import { mdiMenu } from "@mdi/js";
-import { fireEvent } from "../../../src/common/dom/fire_event";
@customElement("hassio-ingress-view")
class HassioIngressView extends LitElement {
diff --git a/hassio/src/snapshots/hassio-snapshots.ts b/hassio/src/snapshots/hassio-snapshots.ts
index d156f567a9..4cce8cc0ff 100644
--- a/hassio/src/snapshots/hassio-snapshots.ts
+++ b/hassio/src/snapshots/hassio-snapshots.ts
@@ -264,7 +264,7 @@ class HassioSnapshots extends LitElement {
}
protected updated(changedProps: PropertyValues) {
- if (changedProps.has("supervisorInfo")) {
+ if (changedProps.has("supervisor")) {
this._addonList = this.supervisor.supervisor.addons
.map((addon) => ({
slug: addon.slug,
diff --git a/hassio/src/system/hassio-core-info.ts b/hassio/src/system/hassio-core-info.ts
new file mode 100644
index 0000000000..96c3aad65f
--- /dev/null
+++ b/hassio/src/system/hassio-core-info.ts
@@ -0,0 +1,246 @@
+import "@material/mwc-button";
+import "@material/mwc-list/mwc-list-item";
+import {
+ css,
+ CSSResult,
+ customElement,
+ html,
+ internalProperty,
+ LitElement,
+ property,
+ TemplateResult,
+} from "lit-element";
+import "../../../src/components/buttons/ha-progress-button";
+import "../../../src/components/ha-button-menu";
+import "../../../src/components/ha-card";
+import "../../../src/components/ha-settings-row";
+import {
+ extractApiErrorMessage,
+ fetchHassioStats,
+ HassioStats,
+} from "../../../src/data/hassio/common";
+import { restartCore, updateCore } from "../../../src/data/supervisor/core";
+import { Supervisor } from "../../../src/data/supervisor/supervisor";
+import {
+ showAlertDialog,
+ showConfirmationDialog,
+} from "../../../src/dialogs/generic/show-dialog-box";
+import { haStyle } from "../../../src/resources/styles";
+import { HomeAssistant } from "../../../src/types";
+import { bytesToString } from "../../../src/util/bytes-to-string";
+import "../components/supervisor-metric";
+import { hassioStyle } from "../resources/hassio-style";
+
+@customElement("hassio-core-info")
+class HassioCoreInfo extends LitElement {
+ @property({ attribute: false }) public hass!: HomeAssistant;
+
+ @property({ attribute: false }) public supervisor!: Supervisor;
+
+ @internalProperty() private _metrics?: HassioStats;
+
+ protected render(): TemplateResult | void {
+ const metrics = [
+ {
+ description: "Core CPU Usage",
+ value: this._metrics?.cpu_percent,
+ },
+ {
+ description: "Core RAM Usage",
+ value: this._metrics?.memory_percent,
+ tooltip: `${bytesToString(this._metrics?.memory_usage)}/${bytesToString(
+ this._metrics?.memory_limit
+ )}`,
+ },
+ ];
+
+ return html`
+
+
+
+
+
+ Version
+
+
+ core-${this.supervisor.core.version}
+
+
+
+
+ Newest Version
+
+
+ core-${this.supervisor.core.version_latest}
+
+ ${this.supervisor.core.update_available
+ ? html`
+
+ Update
+
+ `
+ : ""}
+
+
+
+ ${metrics.map(
+ (metric) =>
+ html`
+
+ `
+ )}
+
+
+
+
+ Restart Core
+
+
+
+ `;
+ }
+
+ protected firstUpdated(): void {
+ this._loadData();
+ }
+
+ private async _loadData(): Promise
{
+ this._metrics = await fetchHassioStats(this.hass, "core");
+ }
+
+ private async _coreRestart(ev: CustomEvent): Promise {
+ const button = ev.currentTarget as any;
+ button.progress = true;
+
+ const confirmed = await showConfirmationDialog(this, {
+ title: "Restart Home Assistant Core",
+ text: "Are you sure you want to restart Home Assistant Core",
+ confirmText: "restart",
+ dismissText: "cancel",
+ });
+
+ if (!confirmed) {
+ button.progress = false;
+ return;
+ }
+
+ try {
+ await restartCore(this.hass);
+ } catch (err) {
+ showAlertDialog(this, {
+ title: "Failed to restart Home Assistant Core",
+ text: extractApiErrorMessage(err),
+ });
+ } finally {
+ button.progress = false;
+ }
+ }
+
+ private async _coreUpdate(ev: CustomEvent): Promise {
+ const button = ev.currentTarget as any;
+ button.progress = true;
+
+ const confirmed = await showConfirmationDialog(this, {
+ title: "Update Home Assistant Core",
+ text: `Are you sure you want to update Home Assistant Core to version ${this.supervisor.core.version_latest}?`,
+ confirmText: "update",
+ dismissText: "cancel",
+ });
+
+ if (!confirmed) {
+ button.progress = false;
+ return;
+ }
+
+ try {
+ await updateCore(this.hass);
+ } catch (err) {
+ showAlertDialog(this, {
+ title: "Failed to update Home Assistant Core",
+ text: extractApiErrorMessage(err),
+ });
+ } finally {
+ button.progress = false;
+ }
+ }
+
+ static get styles(): CSSResult[] {
+ return [
+ haStyle,
+ hassioStyle,
+ css`
+ ha-card {
+ height: 100%;
+ justify-content: space-between;
+ flex-direction: column;
+ display: flex;
+ }
+ .card-actions {
+ height: 48px;
+ border-top: none;
+ display: flex;
+ justify-content: flex-end;
+ align-items: center;
+ }
+ .card-content {
+ display: flex;
+ flex-direction: column;
+ height: calc(100% - 124px);
+ justify-content: space-between;
+ }
+ ha-settings-row {
+ padding: 0;
+ height: 54px;
+ width: 100%;
+ }
+ ha-settings-row[three-line] {
+ height: 74px;
+ }
+ ha-settings-row > span[slot="description"] {
+ white-space: normal;
+ color: var(--secondary-text-color);
+ }
+
+ .warning {
+ --mdc-theme-primary: var(--error-color);
+ }
+
+ ha-button-menu {
+ color: var(--secondary-text-color);
+ --mdc-menu-min-width: 200px;
+ }
+ @media (min-width: 563px) {
+ paper-listbox {
+ max-height: 150px;
+ overflow: auto;
+ }
+ }
+ paper-item {
+ cursor: pointer;
+ min-height: 35px;
+ }
+ mwc-list-item ha-svg-icon {
+ color: var(--secondary-text-color);
+ }
+ `,
+ ];
+ }
+}
+
+declare global {
+ interface HTMLElementTagNameMap {
+ "hassio-core-info": HassioCoreInfo;
+ }
+}
diff --git a/hassio/src/system/hassio-host-info.ts b/hassio/src/system/hassio-host-info.ts
index 42b066c214..773fcc8bab 100644
--- a/hassio/src/system/hassio-host-info.ts
+++ b/hassio/src/system/hassio-host-info.ts
@@ -43,6 +43,11 @@ import {
} from "../../../src/dialogs/generic/show-dialog-box";
import { haStyle } from "../../../src/resources/styles";
import { HomeAssistant } from "../../../src/types";
+import {
+ getValueInPercentage,
+ roundWithOneDecimal,
+} from "../../../src/util/calculate";
+import "../components/supervisor-metric";
import { showHassioMarkdownDialog } from "../dialogs/markdown/show-dialog-hassio-markdown";
import { showNetworkDialog } from "../dialogs/network/show-dialog-network";
import { hassioStyle } from "../resources/hassio-style";
@@ -57,80 +62,105 @@ class HassioHostInfo extends LitElement {
const primaryIpAddress = this.supervisor.host.features.includes("network")
? this._primaryIpAddress(this.supervisor.network!)
: "";
- return html`
-
-
- ${this.supervisor.host.features.includes("hostname")
- ? html`
-
- Hostname
-
-
- ${this.supervisor.host.hostname}
-
-
-
- `
- : ""}
- ${this.supervisor.host.features.includes("network")
- ? html`
-
- IP Address
-
-
- ${primaryIpAddress}
-
-
-
- `
- : ""}
-
-
- Operating System
-
-
- ${this.supervisor.host.operating_system}
-
- ${this.supervisor.os.update_available
- ? html`
-
+
+
+ ${this.supervisor.host.features.includes("hostname")
+ ? html`
+
+ Hostname
+
+
+ ${this.supervisor.host.hostname}
+
+
- Update
-
- `
+
+ `
: ""}
-
- ${!this.supervisor.host.features.includes("hassos")
- ? html`
-
- Docker version
-
-
- ${this.supervisor.info.docker}
-
- `
- : ""}
- ${this.supervisor.host.deployment
- ? html`
-
- Deployment
-
-
- ${this.supervisor.host.deployment}
-
- `
- : ""}
+ ${this.supervisor.host.features.includes("network")
+ ? html`
+
+ IP Address
+
+
+ ${primaryIpAddress}
+
+
+
+ `
+ : ""}
+
+
+
+ Operating System
+
+
+ ${this.supervisor.host.operating_system}
+
+ ${this.supervisor.os.update_available
+ ? html`
+
+ Update
+
+ `
+ : ""}
+
+ ${!this.supervisor.host.features.includes("hassos")
+ ? html`
+
+ Docker version
+
+
+ ${this.supervisor.info.docker}
+
+ `
+ : ""}
+ ${this.supervisor.host.deployment
+ ? html`
+
+ Deployment
+
+
+ ${this.supervisor.host.deployment}
+
+ `
+ : ""}
+
+
+ ${metrics.map(
+ (metric) =>
+ html`
+
+ `
+ )}
+
${this.supervisor.host.features.includes("reboot")
@@ -140,7 +170,7 @@ class HassioHostInfo extends LitElement {
class="warning"
@click=${this._hostReboot}
>
- Reboot
+ Reboot Host
`
: ""}
@@ -151,7 +181,7 @@ class HassioHostInfo extends LitElement {
class="warning"
@click=${this._hostShutdown}
>
- Shutdown
+ Shutdown Host
`
: ""}
@@ -183,6 +213,10 @@ class HassioHostInfo extends LitElement {
this._loadData();
}
+ private _getUsedSpace = memoizeOne((used: number, total: number) =>
+ roundWithOneDecimal(getValueInPercentage(used, 0, total))
+ );
+
private _primaryIpAddress = memoizeOne((network_info: NetworkInfo) => {
if (!network_info || !network_info.interfaces) {
return "";
@@ -369,6 +403,12 @@ class HassioHostInfo extends LitElement {
justify-content: space-between;
align-items: center;
}
+ .card-content {
+ display: flex;
+ flex-direction: column;
+ height: calc(100% - 124px);
+ justify-content: space-between;
+ }
ha-settings-row {
padding: 0;
height: 54px;
diff --git a/hassio/src/system/hassio-supervisor-info.ts b/hassio/src/system/hassio-supervisor-info.ts
index a64f8e2d73..b29252d2ad 100644
--- a/hassio/src/system/hassio-supervisor-info.ts
+++ b/hassio/src/system/hassio-supervisor-info.ts
@@ -3,6 +3,7 @@ import {
CSSResult,
customElement,
html,
+ internalProperty,
LitElement,
property,
TemplateResult,
@@ -12,7 +13,11 @@ import "../../../src/components/buttons/ha-progress-button";
import "../../../src/components/ha-card";
import "../../../src/components/ha-settings-row";
import "../../../src/components/ha-switch";
-import { extractApiErrorMessage } from "../../../src/data/hassio/common";
+import {
+ extractApiErrorMessage,
+ fetchHassioStats,
+ HassioStats,
+} from "../../../src/data/hassio/common";
import {
fetchHassioSupervisorInfo,
reloadSupervisor,
@@ -28,7 +33,9 @@ import {
} from "../../../src/dialogs/generic/show-dialog-box";
import { haStyle } from "../../../src/resources/styles";
import { HomeAssistant } from "../../../src/types";
+import { bytesToString } from "../../../src/util/bytes-to-string";
import { documentationUrl } from "../../../src/util/documentation-url";
+import "../components/supervisor-metric";
import { hassioStyle } from "../resources/hassio-style";
const UNSUPPORTED_REASON = {
@@ -87,127 +94,164 @@ class HassioSupervisorInfo extends LitElement {
@property({ attribute: false }) public supervisor!: Supervisor;
+ @internalProperty() private _metrics?: HassioStats;
+
protected render(): TemplateResult | void {
+ const metrics = [
+ {
+ description: "Supervisor CPU Usage",
+ value: this._metrics?.cpu_percent,
+ },
+ {
+ description: "Supervisor RAM Usage",
+ value: this._metrics?.memory_percent,
+ tooltip: `${bytesToString(this._metrics?.memory_usage)}/${bytesToString(
+ this._metrics?.memory_limit
+ )}`,
+ },
+ ];
return html`
-
-
- Version
-
-
- ${this.supervisor.supervisor.version}
-
-
-
-
- Newest Version
-
-
- ${this.supervisor.supervisor.version_latest}
-
- ${this.supervisor.supervisor.update_available
- ? html`
-
- Update
-
- `
- : ""}
-
-
-
- Channel
-
-
- ${this.supervisor.supervisor.channel}
-
- ${this.supervisor.supervisor.channel === "beta"
- ? html`
-
- Leave beta channel
-
- `
- : this.supervisor.supervisor.channel === "stable"
- ? html`
-
- Join beta channel
-
- `
- : ""}
-
+
+
+
+ Version
+
+
+ supervisor-${this.supervisor.supervisor.version}
+
+
+
+
+ Newest Version
+
+
+ supervisor-${this.supervisor.supervisor.version_latest}
+
+ ${this.supervisor.supervisor.update_available
+ ? html`
+
+ Update
+
+ `
+ : ""}
+
+
+
+ Channel
+
+
+ ${this.supervisor.supervisor.channel}
+
+ ${this.supervisor.supervisor.channel === "beta"
+ ? html`
+
+ Leave beta channel
+
+ `
+ : this.supervisor.supervisor.channel === "stable"
+ ? html`
+
+ Join beta channel
+
+ `
+ : ""}
+
- ${this.supervisor.supervisor.supported
- ? html`
-
- Share Diagnostics
-
-
- Share crash reports and diagnostic information.
+ ${this.supervisor.supervisor.supported
+ ? html`
+
+ Share Diagnostics
+
+
+ Share crash reports and diagnostic information.
+
+
+
+ `
+ : html`
+ You are running an unsupported installation.
-
-
- `
- : html`
- You are running an unsupported installation.
-
-
`}
- ${!this.supervisor.supervisor.healthy
- ? html`
- Your installation is running in an unhealthy state.
-
-
`
- : ""}
+
`}
+ ${!this.supervisor.supervisor.healthy
+ ? html`
+ Your installation is running in an unhealthy state.
+
+
`
+ : ""}
+
+
+ ${metrics.map(
+ (metric) =>
+ html`
+
+ `
+ )}
+
- Reload
+ Reload Supervisor
- Restart
+ Restart Supervisor
`;
}
+ protected firstUpdated(): void {
+ this._loadData();
+ }
+
+ private async _loadData(): Promise
{
+ this._metrics = await fetchHassioStats(this.hass, "supervisor");
+ }
+
private async _toggleBeta(ev: CustomEvent): Promise {
const button = ev.currentTarget as any;
button.progress = true;
@@ -282,6 +326,18 @@ class HassioSupervisorInfo extends LitElement {
const button = ev.currentTarget as any;
button.progress = true;
+ const confirmed = await showConfirmationDialog(this, {
+ title: "Restart the Supervisor",
+ text: "Are you sure you want to restart the Supervisor",
+ confirmText: "restart",
+ dismissText: "cancel",
+ });
+
+ if (!confirmed) {
+ button.progress = false;
+ return;
+ }
+
try {
await restartSupervisor(this.hass);
} catch (err) {
@@ -426,6 +482,15 @@ class HassioSupervisorInfo extends LitElement {
justify-content: space-between;
align-items: center;
}
+ .card-content {
+ display: flex;
+ flex-direction: column;
+ height: calc(100% - 124px);
+ justify-content: space-between;
+ }
+ .metrics-block {
+ margin-top: 16px;
+ }
button.link {
color: var(--primary-color);
}
diff --git a/hassio/src/system/hassio-system-metrics.ts b/hassio/src/system/hassio-system-metrics.ts
deleted file mode 100644
index 8cd4450e90..0000000000
--- a/hassio/src/system/hassio-system-metrics.ts
+++ /dev/null
@@ -1,185 +0,0 @@
-import "@material/mwc-button";
-import "@material/mwc-list/mwc-list-item";
-import {
- css,
- CSSResult,
- customElement,
- html,
- internalProperty,
- LitElement,
- property,
- TemplateResult,
-} from "lit-element";
-import { classMap } from "lit-html/directives/class-map";
-import memoizeOne from "memoize-one";
-import "../../../src/components/buttons/ha-progress-button";
-import "../../../src/components/ha-bar";
-import "../../../src/components/ha-button-menu";
-import "../../../src/components/ha-card";
-import "../../../src/components/ha-settings-row";
-import { fetchHassioStats, HassioStats } from "../../../src/data/hassio/common";
-import { HassioHostInfo } from "../../../src/data/hassio/host";
-import { Supervisor } from "../../../src/data/supervisor/supervisor";
-import { haStyle } from "../../../src/resources/styles";
-import { HomeAssistant } from "../../../src/types";
-import { bytesToString } from "../../../src/util/bytes-to-string";
-import {
- getValueInPercentage,
- roundWithOneDecimal,
-} from "../../../src/util/calculate";
-import { hassioStyle } from "../resources/hassio-style";
-
-@customElement("hassio-system-metrics")
-class HassioSystemMetrics extends LitElement {
- @property({ attribute: false }) public hass!: HomeAssistant;
-
- @property({ attribute: false }) public supervisor!: Supervisor;
-
- @internalProperty() private _supervisorMetrics?: HassioStats;
-
- @internalProperty() private _coreMetrics?: HassioStats;
-
- protected render(): TemplateResult | void {
- const metrics = [
- {
- description: "Core CPU Usage",
- value: this._coreMetrics?.cpu_percent,
- },
- {
- description: "Core RAM Usage",
- value: this._coreMetrics?.memory_percent,
- tooltip: `${bytesToString(
- this._coreMetrics?.memory_usage
- )}/${bytesToString(this._coreMetrics?.memory_limit)}`,
- },
- {
- description: "Supervisor CPU Usage",
- value: this._supervisorMetrics?.cpu_percent,
- },
- {
- description: "Supervisor RAM Usage",
- value: this._supervisorMetrics?.memory_percent,
- tooltip: `${bytesToString(
- this._supervisorMetrics?.memory_usage
- )}/${bytesToString(this._supervisorMetrics?.memory_limit)}`,
- },
- {
- description: "Used Space",
- value: this._getUsedSpace(this.supervisor.host),
- tooltip: `${this.supervisor.host.disk_used} GB/${this.supervisor.host.disk_total} GB`,
- },
- ];
-
- return html`
-
-
- ${metrics.map((metric) =>
- this._renderMetric(
- metric.description,
- metric.value ?? 0,
- metric.tooltip
- )
- )}
-
-
- `;
- }
-
- protected firstUpdated(): void {
- this._loadData();
- }
-
- private _renderMetric(
- description: string,
- value: number,
- tooltip?: string
- ): TemplateResult {
- const roundedValue = roundWithOneDecimal(value);
- return html`
-
- ${description}
-
-
-
- ${roundedValue}%
-
- 50,
- "target-critical": roundedValue > 85,
- })}"
- .value=${value}
- >
-
- `;
- }
-
- private _getUsedSpace = memoizeOne((hostInfo: HassioHostInfo) =>
- roundWithOneDecimal(
- getValueInPercentage(hostInfo.disk_used, 0, hostInfo.disk_total)
- )
- );
-
- private async _loadData(): Promise {
- const [supervisor, core] = await Promise.all([
- fetchHassioStats(this.hass, "supervisor"),
- fetchHassioStats(this.hass, "core"),
- ]);
- this._supervisorMetrics = supervisor;
- this._coreMetrics = core;
- }
-
- static get styles(): CSSResult[] {
- return [
- haStyle,
- hassioStyle,
- css`
- ha-card {
- height: 100%;
- justify-content: space-between;
- flex-direction: column;
- display: flex;
- }
- ha-settings-row {
- padding: 0;
- height: 54px;
- width: 100%;
- }
- ha-settings-row > div[slot="description"] {
- white-space: normal;
- color: var(--secondary-text-color);
- display: flex;
- justify-content: space-between;
- }
- ha-bar {
- --ha-bar-primary-color: var(
- --hassio-bar-ok-color,
- var(--success-color)
- );
- }
- .target-warning {
- --ha-bar-primary-color: var(
- --hassio-bar-warning-color,
- var(--warning-color)
- );
- }
- .target-critical {
- --ha-bar-primary-color: var(
- --hassio-bar-critical-color,
- var(--error-color)
- );
- }
- .value {
- width: 42px;
- padding-right: 4px;
- }
- `,
- ];
- }
-}
-
-declare global {
- interface HTMLElementTagNameMap {
- "hassio-system-metrics": HassioSystemMetrics;
- }
-}
diff --git a/hassio/src/system/hassio-system.ts b/hassio/src/system/hassio-system.ts
index 9c44de311b..fa9999485e 100644
--- a/hassio/src/system/hassio-system.ts
+++ b/hassio/src/system/hassio-system.ts
@@ -13,10 +13,10 @@ import { haStyle } from "../../../src/resources/styles";
import { HomeAssistant, Route } from "../../../src/types";
import { supervisorTabs } from "../hassio-tabs";
import { hassioStyle } from "../resources/hassio-style";
+import "./hassio-core-info";
import "./hassio-host-info";
import "./hassio-supervisor-info";
import "./hassio-supervisor-log";
-import "./hassio-system-metrics";
@customElement("hassio-system")
class HassioSystem extends LitElement {
@@ -41,6 +41,10 @@ class HassioSystem extends LitElement {
System
diff --git a/package.json b/package.json
index 6349d3864f..f6e9c22437 100644
--- a/package.json
+++ b/package.json
@@ -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",
@@ -120,7 +120,7 @@
"resize-observer-polyfill": "^1.5.1",
"roboto-fontface": "^0.10.0",
"sortablejs": "^1.10.2",
- "superstruct": "^0.10.12",
+ "superstruct": "^0.10.13",
"tinykeys": "^1.1.1",
"unfetch": "^4.1.0",
"vis-data": "^7.1.1",
diff --git a/setup.py b/setup.py
index 3e58b0b9ad..c0915cc7e9 100644
--- a/setup.py
+++ b/setup.py
@@ -2,7 +2,7 @@ from setuptools import setup, find_packages
setup(
name="home-assistant-frontend",
- version="20201229.1",
+ version="20210127.0",
description="The Home Assistant frontend",
url="https://github.com/home-assistant/home-assistant-polymer",
author="The Home Assistant Authors",
diff --git a/src/auth/ha-auth-flow.ts b/src/auth/ha-auth-flow.ts
index a4234c56ca..a4092f0e41 100644
--- a/src/auth/ha-auth-flow.ts
+++ b/src/auth/ha-auth-flow.ts
@@ -3,9 +3,9 @@ import {
css,
CSSResult,
html,
+ internalProperty,
LitElement,
property,
- internalProperty,
PropertyValues,
TemplateResult,
} from "lit-element";
diff --git a/src/auth/ha-authorize.ts b/src/auth/ha-authorize.ts
index 0d5cdd4181..3cd85aa5f2 100644
--- a/src/auth/ha-authorize.ts
+++ b/src/auth/ha-authorize.ts
@@ -2,21 +2,25 @@ import {
css,
CSSResult,
html,
+ internalProperty,
LitElement,
property,
- internalProperty,
PropertyValues,
} from "lit-element";
+import punycode from "punycode";
+import { extractSearchParamsObject } from "../common/url/search-params";
import {
AuthProvider,
- fetchAuthProviders,
AuthUrlSearchParams,
+ fetchAuthProviders,
} from "../data/auth";
+import {
+ DiscoveryInformation,
+ fetchDiscoveryInformation,
+} from "../data/discovery";
import { litLocalizeLiteMixin } from "../mixins/lit-localize-lite-mixin";
import { registerServiceWorker } from "../util/register-service-worker";
import "./ha-auth-flow";
-import { extractSearchParamsObject } from "../common/url/search-params";
-import punycode from "punycode";
import("./ha-pick-auth-provider");
@@ -31,6 +35,8 @@ class HaAuthorize extends litLocalizeLiteMixin(LitElement) {
@internalProperty() private _authProviders?: AuthProvider[];
+ @internalProperty() private _discovery?: DiscoveryInformation;
+
constructor() {
super();
this.translationFragment = "page-authorize";
@@ -58,14 +64,17 @@ class HaAuthorize extends litLocalizeLiteMixin(LitElement) {
// the name with a bold tag.
const loggingInWith = document.createElement("div");
loggingInWith.innerText = this.localize(
- "ui.panel.page-authorize.logging_in_with",
+ this._discovery?.location_name
+ ? "ui.panel.page-authorize.logging_in_to_with"
+ : "ui.panel.page-authorize.logging_in_with",
+ "locationName",
+ "LOCATION",
"authProviderName",
"NAME"
);
- loggingInWith.innerHTML = loggingInWith.innerHTML.replace(
- "**NAME**",
- `${this._authProvider!.name}`
- );
+ loggingInWith.innerHTML = loggingInWith.innerHTML
+ .replace("**LOCATION**", `${this._discovery?.location_name}`)
+ .replace("**NAME**", `${this._authProvider!.name}`);
const inactiveProviders = this._authProviders.filter(
(prv) => prv !== this._authProvider
@@ -105,6 +114,7 @@ class HaAuthorize extends litLocalizeLiteMixin(LitElement) {
protected firstUpdated(changedProps: PropertyValues) {
super.firstUpdated(changedProps);
this._fetchAuthProviders();
+ this._fetchDiscoveryInfo();
if (!this.redirectUri) {
return;
@@ -126,6 +136,10 @@ class HaAuthorize extends litLocalizeLiteMixin(LitElement) {
}
}
+ private async _fetchDiscoveryInfo() {
+ this._discovery = await fetchDiscoveryInformation();
+ }
+
private async _fetchAuthProviders() {
// Fetch auth providers
try {
diff --git a/src/common/config/can_show_page.ts b/src/common/config/can_show_page.ts
index 0ac4f6d2ec..05e96d43e2 100644
--- a/src/common/config/can_show_page.ts
+++ b/src/common/config/can_show_page.ts
@@ -1,6 +1,6 @@
-import { isComponentLoaded } from "./is_component_loaded";
import { PageNavigation } from "../../layouts/hass-tabs-subpage";
import { HomeAssistant } from "../../types";
+import { isComponentLoaded } from "./is_component_loaded";
export const canShowPage = (hass: HomeAssistant, page: PageNavigation) => {
return (
diff --git a/src/common/const.ts b/src/common/const.ts
index 4b4f8e6225..bbf051f668 100644
--- a/src/common/const.ts
+++ b/src/common/const.ts
@@ -34,6 +34,7 @@ export const FIXED_DOMAIN_ICONS = {
light: "hass:lightbulb",
mailbox: "hass:mailbox",
notify: "hass:comment-alert",
+ number: "hass:ray-vertex",
persistent_notification: "hass:bell",
person: "hass:account",
plant: "hass:flower",
@@ -77,6 +78,7 @@ export const DOMAINS_WITH_CARD = [
"input_text",
"lock",
"media_player",
+ "number",
"scene",
"script",
"timer",
@@ -114,6 +116,7 @@ export const DOMAINS_HIDE_MORE_INFO = [
"input_number",
"input_select",
"input_text",
+ "number",
"scene",
];
@@ -138,6 +141,9 @@ export const DOMAINS_TOGGLE = new Set([
"humidifier",
]);
+/** Domains that have a dynamic entity image / picture. */
+export const DOMAINS_WITH_DYNAMIC_PICTURE = new Set(["camera", "media_player"]);
+
/** Temperature units. */
export const UNIT_C = "°C";
export const UNIT_F = "°F";
diff --git a/src/common/datetime/check_valid_date.ts b/src/common/datetime/check_valid_date.ts
new file mode 100644
index 0000000000..e380a9b79f
--- /dev/null
+++ b/src/common/datetime/check_valid_date.ts
@@ -0,0 +1,7 @@
+export default function checkValidDate(date?: Date): boolean {
+ if (!date) {
+ return false;
+ }
+
+ return date instanceof Date && !isNaN(date.valueOf());
+}
diff --git a/src/common/datetime/format_date.ts b/src/common/datetime/format_date.ts
index d8c46d4e8d..050006fe7d 100644
--- a/src/common/datetime/format_date.ts
+++ b/src/common/datetime/format_date.ts
@@ -9,3 +9,12 @@ export const formatDate = toLocaleDateStringSupportsOptions
day: "numeric",
})
: (dateObj: Date) => format(dateObj, "longDate");
+
+export const formatDateWeekday = toLocaleDateStringSupportsOptions
+ ? (dateObj: Date, locales: string) =>
+ dateObj.toLocaleDateString(locales, {
+ weekday: "long",
+ month: "short",
+ day: "numeric",
+ })
+ : (dateObj: Date) => format(dateObj, "dddd, MMM D");
diff --git a/src/common/datetime/format_time.ts b/src/common/datetime/format_time.ts
index c27874f3a6..6b79f0c173 100644
--- a/src/common/datetime/format_time.ts
+++ b/src/common/datetime/format_time.ts
@@ -17,3 +17,12 @@ export const formatTimeWithSeconds = toLocaleTimeStringSupportsOptions
second: "2-digit",
})
: (dateObj: Date) => format(dateObj, "mediumTime");
+
+export const formatTimeWeekday = toLocaleTimeStringSupportsOptions
+ ? (dateObj: Date, locales: string) =>
+ dateObj.toLocaleTimeString(locales, {
+ weekday: "long",
+ hour: "numeric",
+ minute: "2-digit",
+ })
+ : (dateObj: Date) => format(dateObj, "dddd, HH:mm");
diff --git a/src/common/entity/compute_state_display.ts b/src/common/entity/compute_state_display.ts
index f946d34015..91b7a606dc 100644
--- a/src/common/entity/compute_state_display.ts
+++ b/src/common/entity/compute_state_display.ts
@@ -3,9 +3,9 @@ import { UNAVAILABLE, UNKNOWN } from "../../data/entity";
import { formatDate } from "../datetime/format_date";
import { formatDateTime } from "../datetime/format_date_time";
import { formatTime } from "../datetime/format_time";
+import { formatNumber } from "../string/format_number";
import { LocalizeFunc } from "../translations/localize";
import { computeStateDomain } from "./compute_state_domain";
-import { formatNumber } from "../string/format_number";
export const computeStateDisplay = (
localize: LocalizeFunc,
@@ -63,7 +63,7 @@ export const computeStateDisplay = (
if (domain === "humidifier") {
if (compareState === "on" && stateObj.attributes.humidity) {
- return `${stateObj.attributes.humidity}%`;
+ return `${stateObj.attributes.humidity} %`;
}
}
diff --git a/src/common/entity/state_card_type.ts b/src/common/entity/state_card_type.ts
index 2013ee2058..a1f2aa561e 100644
--- a/src/common/entity/state_card_type.ts
+++ b/src/common/entity/state_card_type.ts
@@ -1,9 +1,9 @@
import { HassEntity } from "home-assistant-js-websocket";
+import { UNAVAILABLE } from "../../data/entity";
import { HomeAssistant } from "../../types";
import { DOMAINS_WITH_CARD } from "../const";
import { canToggleState } from "./can_toggle_state";
import { computeStateDomain } from "./compute_state_domain";
-import { UNAVAILABLE } from "../../data/entity";
export const stateCardType = (hass: HomeAssistant, stateObj: HassEntity) => {
if (stateObj.state === UNAVAILABLE) {
diff --git a/src/common/image/extract_color.ts b/src/common/image/extract_color.ts
index e9561bcc8c..96497100ed 100644
--- a/src/common/image/extract_color.ts
+++ b/src/common/image/extract_color.ts
@@ -1,7 +1,7 @@
-import Vibrant from "node-vibrant/lib/browser";
-import MMCQ from "@vibrant/quantizer-mmcq";
-import { BasicPipeline } from "@vibrant/core/lib/pipeline";
-import { Swatch, Vec3 } from "@vibrant/color";
+// We import the minified bundle because the unminified bundle
+// has some quirks that break wds. See #7784 for unminified version.
+import Vibrant from "node-vibrant/dist/vibrant";
+import type { Swatch, Vec3 } from "@vibrant/color";
import { getRGBContrastRatio } from "../color/rgb";
const CONTRAST_RATIO = 4.5;
@@ -104,23 +104,15 @@ const customGenerator = (colors: Swatch[]) => {
}
return {
- foreground: new Swatch(foregroundColor, 0),
+ // We can't import Swatch constructor from the minified bundle, take it from background color.
+ // @ts-expect-error
+ foreground: new backgroundColor.constructor(foregroundColor, 0),
background: backgroundColor,
};
};
-Vibrant.use(
- new BasicPipeline().filter
- .register(
- "default",
- (r: number, g: number, b: number, a: number) =>
- a >= 125 && !(r > 250 && g > 250 && b > 250)
- )
- .quantizer.register("mmcq", MMCQ)
- // Our generator has different output
- // @ts-expect-error
- .generator.register("default", customGenerator)
-);
+// Set our custom generator as the default.
+Vibrant._pipeline.generator.register("default", customGenerator);
export const extractColors = (url: string, downsampleColors = 16) =>
new Vibrant(url, {
diff --git a/src/common/search/search-input.ts b/src/common/search/search-input.ts
index 97a52b1864..aff3a1a2a7 100644
--- a/src/common/search/search-input.ts
+++ b/src/common/search/search-input.ts
@@ -1,3 +1,5 @@
+import "@material/mwc-icon-button/mwc-icon-button";
+import { mdiClose, mdiMagnify } from "@mdi/js";
import "@polymer/paper-input/paper-input";
import {
css,
@@ -10,8 +12,6 @@ import { html, TemplateResult } from "lit-html";
import { classMap } from "lit-html/directives/class-map";
import "../../components/ha-svg-icon";
import { fireEvent } from "../dom/fire_event";
-import { mdiMagnify, mdiClose } from "@mdi/js";
-import "@material/mwc-icon-button/mwc-icon-button";
@customElement("search-input")
class SearchInput extends LitElement {
diff --git a/src/common/structs/handle-errors.ts b/src/common/structs/handle-errors.ts
new file mode 100644
index 0000000000..7aa8889045
--- /dev/null
+++ b/src/common/structs/handle-errors.ts
@@ -0,0 +1,45 @@
+import { StructError } from "superstruct";
+import type { HomeAssistant } from "../../types";
+
+export const handleStructError = (
+ hass: HomeAssistant,
+ err: Error
+): { warnings: string[]; errors?: string[] } => {
+ if (!(err instanceof StructError)) {
+ return { warnings: [err.message], errors: undefined };
+ }
+ const errors: string[] = [];
+ const warnings: string[] = [];
+ for (const failure of err.failures()) {
+ if (failure.value === undefined) {
+ errors.push(
+ hass.localize(
+ "ui.errors.config.key_missing",
+ "key",
+ failure.path.join(".")
+ )
+ );
+ } else if (failure.type === "never") {
+ warnings.push(
+ hass.localize(
+ "ui.errors.config.key_not_expected",
+ "key",
+ failure.path.join(".")
+ )
+ );
+ } else {
+ warnings.push(
+ hass.localize(
+ "ui.errors.config.key_wrong_type",
+ "key",
+ failure.path.join("."),
+ "type_correct",
+ failure.type,
+ "type_wrong",
+ JSON.stringify(failure.value)
+ )
+ );
+ }
+ }
+ return { warnings, errors };
+};
diff --git a/src/common/structs/is-entity-id.ts b/src/common/structs/is-entity-id.ts
new file mode 100644
index 0000000000..c408187e84
--- /dev/null
+++ b/src/common/structs/is-entity-id.ts
@@ -0,0 +1,30 @@
+import { struct, StructContext, StructResult } from "superstruct";
+
+const isEntityId = (value: unknown, context: StructContext): StructResult => {
+ if (typeof value !== "string") {
+ return [context.fail({ type: "string" })];
+ }
+ if (!value.includes(".")) {
+ return [
+ context.fail({
+ type: "Entity ID should be in the format 'domain.entity'",
+ }),
+ ];
+ }
+ return true;
+};
+
+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);
diff --git a/src/panels/lovelace/common/structs/is-icon.ts b/src/common/structs/is-icon.ts
similarity index 85%
rename from src/panels/lovelace/common/structs/is-icon.ts
rename to src/common/structs/is-icon.ts
index d88dcdb593..7ca4ff9ed5 100644
--- a/src/panels/lovelace/common/structs/is-icon.ts
+++ b/src/common/structs/is-icon.ts
@@ -1,4 +1,4 @@
-import { StructContext, StructResult, struct } from "superstruct";
+import { struct, StructContext, StructResult } from "superstruct";
const isIcon = (value: unknown, context: StructContext): StructResult => {
if (typeof value !== "string") {
diff --git a/src/common/translations/localize.ts b/src/common/translations/localize.ts
index 47bbca7692..94edcc8a7a 100644
--- a/src/common/translations/localize.ts
+++ b/src/common/translations/localize.ts
@@ -1,5 +1,5 @@
-import IntlMessageFormat from "intl-messageformat";
import { shouldPolyfill } from "@formatjs/intl-pluralrules/should-polyfill";
+import IntlMessageFormat from "intl-messageformat";
import { Resources } from "../../types";
export type LocalizeFunc = (key: string, ...args: any[]) => string;
diff --git a/src/common/util/copy-clipboard.ts b/src/common/util/copy-clipboard.ts
index 3c89e40982..1708858c85 100644
--- a/src/common/util/copy-clipboard.ts
+++ b/src/common/util/copy-clipboard.ts
@@ -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);
};
diff --git a/src/components/buttons/ha-progress-button.ts b/src/components/buttons/ha-progress-button.ts
index c6c325fdab..bb431cf7cb 100644
--- a/src/components/buttons/ha-progress-button.ts
+++ b/src/components/buttons/ha-progress-button.ts
@@ -7,10 +7,9 @@ import {
html,
LitElement,
property,
- TemplateResult,
query,
+ TemplateResult,
} from "lit-element";
-
import "../ha-circular-progress";
@customElement("ha-progress-button")
diff --git a/src/components/data-table/sort-filter.ts b/src/components/data-table/sort-filter.ts
index 79a41e6d77..6fcd8e522a 100644
--- a/src/components/data-table/sort-filter.ts
+++ b/src/components/data-table/sort-filter.ts
@@ -1,5 +1,4 @@
import { wrap } from "comlink";
-
import type { api } from "./sort_filter_worker";
type FilterDataType = api["filterData"];
diff --git a/src/components/date-range-picker.ts b/src/components/date-range-picker.ts
index 70211da366..f7ec87169e 100644
--- a/src/components/date-range-picker.ts
+++ b/src/components/date-range-picker.ts
@@ -1,11 +1,11 @@
// @ts-nocheck
-import Vue from "vue";
import wrap from "@vue/web-component-wrapper";
+import { customElement } from "lit-element/lib/decorators";
+import Vue from "vue";
import DateRangePicker from "vue2-daterange-picker";
import dateRangePickerStyles from "vue2-daterange-picker/dist/vue2-daterange-picker.css";
import { fireEvent } from "../common/dom/fire_event";
import { Constructor } from "../types";
-import { customElement } from "lit-element/lib/decorators";
const Component = Vue.extend({
props: {
@@ -210,7 +210,7 @@ class DateRangePickerElement extends WrappedElement {
}
.calendar-table {
padding: 0 !important;
- }
+ }
`;
const shadowRoot = this.shadowRoot!;
shadowRoot.appendChild(style);
diff --git a/src/components/device/ha-area-devices-picker.ts b/src/components/device/ha-area-devices-picker.ts
index ed3d5c25f3..6e635d0720 100644
--- a/src/components/device/ha-area-devices-picker.ts
+++ b/src/components/device/ha-area-devices-picker.ts
@@ -1,5 +1,6 @@
-import "@material/mwc-icon-button/mwc-icon-button";
import "@material/mwc-button/mwc-button";
+import "@material/mwc-icon-button/mwc-icon-button";
+import { mdiClose, mdiMenuDown, mdiMenuUp } from "@mdi/js";
import "@polymer/paper-input/paper-input";
import "@polymer/paper-item/paper-item";
import "@polymer/paper-item/paper-item-body";
@@ -11,9 +12,9 @@ import {
CSSResult,
customElement,
html,
+ internalProperty,
LitElement,
property,
- internalProperty,
PropertyValues,
TemplateResult,
} from "lit-element";
@@ -37,9 +38,8 @@ import {
import { SubscribeMixin } from "../../mixins/subscribe-mixin";
import { PolymerChangedEvent } from "../../polymer-types";
import { HomeAssistant } from "../../types";
-import "./ha-devices-picker";
import "../ha-svg-icon";
-import { mdiClose, mdiMenuDown, mdiMenuUp } from "@mdi/js";
+import "./ha-devices-picker";
interface DevicesByArea {
[areaId: string]: AreaDevices;
diff --git a/src/components/device/ha-device-automation-picker.ts b/src/components/device/ha-device-automation-picker.ts
index fe56884a09..dba3b7b224 100644
--- a/src/components/device/ha-device-automation-picker.ts
+++ b/src/components/device/ha-device-automation-picker.ts
@@ -6,9 +6,9 @@ import {
css,
CSSResult,
html,
+ internalProperty,
LitElement,
property,
- internalProperty,
TemplateResult,
} from "lit-element";
import { fireEvent } from "../../common/dom/fire_event";
diff --git a/src/components/device/ha-device-picker.ts b/src/components/device/ha-device-picker.ts
index c1e86dd761..0139a08c42 100644
--- a/src/components/device/ha-device-picker.ts
+++ b/src/components/device/ha-device-picker.ts
@@ -1,5 +1,5 @@
-import "../ha-svg-icon";
import "@material/mwc-icon-button/mwc-icon-button";
+import { mdiClose, mdiMenuDown, mdiMenuUp } from "@mdi/js";
import "@polymer/paper-input/paper-input";
import "@polymer/paper-item/paper-item";
import "@polymer/paper-item/paper-item-body";
@@ -38,7 +38,7 @@ import {
import { SubscribeMixin } from "../../mixins/subscribe-mixin";
import { PolymerChangedEvent } from "../../polymer-types";
import { HomeAssistant } from "../../types";
-import { mdiClose, mdiMenuUp, mdiMenuDown } from "@mdi/js";
+import "../ha-svg-icon";
interface Device {
name: string;
diff --git a/src/components/entity/ha-battery-icon.ts b/src/components/entity/ha-battery-icon.ts
index 1838f6af5e..45f4f7d553 100644
--- a/src/components/entity/ha-battery-icon.ts
+++ b/src/components/entity/ha-battery-icon.ts
@@ -1,6 +1,6 @@
+import { customElement, html, LitElement, property } from "lit-element";
import { batteryIcon } from "../../common/entity/battery_icon";
import "../ha-icon";
-import { customElement, html, property, LitElement } from "lit-element";
@customElement("ha-battery-icon")
export class HaBatteryIcon extends LitElement {
diff --git a/src/components/entity/ha-entity-attribute-picker.ts b/src/components/entity/ha-entity-attribute-picker.ts
index 5b1dbfc080..784ea421f8 100644
--- a/src/components/entity/ha-entity-attribute-picker.ts
+++ b/src/components/entity/ha-entity-attribute-picker.ts
@@ -1,3 +1,4 @@
+import "@material/mwc-icon-button/mwc-icon-button";
import { mdiClose, mdiMenuDown, mdiMenuUp } from "@mdi/js";
import "@polymer/paper-input/paper-input";
import "@polymer/paper-item/paper-item";
@@ -17,10 +18,9 @@ import {
import { fireEvent } from "../../common/dom/fire_event";
import { PolymerChangedEvent } from "../../polymer-types";
import { HomeAssistant } from "../../types";
+import { formatAttributeName } from "../../util/hass-attributes-util";
import "../ha-svg-icon";
import "./state-badge";
-import { formatAttributeName } from "../../util/hass-attributes-util";
-import "@material/mwc-icon-button/mwc-icon-button";
export type HaEntityPickerEntityFilterFunc = (entityId: HassEntity) => boolean;
diff --git a/src/components/entity/ha-entity-toggle.ts b/src/components/entity/ha-entity-toggle.ts
index ca46ad32b0..93bd745e87 100644
--- a/src/components/entity/ha-entity-toggle.ts
+++ b/src/components/entity/ha-entity-toggle.ts
@@ -1,21 +1,21 @@
-import "../ha-icon-button";
import { HassEntity } from "home-assistant-js-websocket";
import {
css,
CSSResult,
html,
+ internalProperty,
LitElement,
property,
- internalProperty,
PropertyValues,
TemplateResult,
} from "lit-element";
import { STATES_OFF } from "../../common/const";
import { computeStateDomain } from "../../common/entity/compute_state_domain";
import { computeStateName } from "../../common/entity/compute_state_name";
-import { UNAVAILABLE_STATES, UNAVAILABLE } from "../../data/entity";
+import { UNAVAILABLE, UNAVAILABLE_STATES } from "../../data/entity";
import { forwardHaptic } from "../../data/haptics";
import { HomeAssistant } from "../../types";
+import "../ha-icon-button";
import "../ha-switch";
const isOn = (stateObj?: HassEntity) =>
diff --git a/src/components/entity/ha-state-label-badge.ts b/src/components/entity/ha-state-label-badge.ts
index 11a4761a2c..86e4d899b1 100644
--- a/src/components/entity/ha-state-label-badge.ts
+++ b/src/components/entity/ha-state-label-badge.ts
@@ -4,9 +4,9 @@ import {
CSSResult,
customElement,
html,
+ internalProperty,
LitElement,
property,
- internalProperty,
PropertyValues,
TemplateResult,
} from "lit-element";
@@ -18,10 +18,10 @@ import { computeStateName } from "../../common/entity/compute_state_name";
import { domainIcon } from "../../common/entity/domain_icon";
import { stateIcon } from "../../common/entity/state_icon";
import { timerTimeRemaining } from "../../common/entity/timer_time_remaining";
+import { formatNumber } from "../../common/string/format_number";
+import { UNAVAILABLE, UNKNOWN } from "../../data/entity";
import { HomeAssistant } from "../../types";
import "../ha-label-badge";
-import { UNAVAILABLE, UNKNOWN } from "../../data/entity";
-import { formatNumber } from "../../common/string/format_number";
@customElement("ha-state-label-badge")
export class HaStateLabelBadge extends LitElement {
diff --git a/src/components/entity/state-badge.ts b/src/components/entity/state-badge.ts
index 200b125e90..4711bf4dfc 100644
--- a/src/components/entity/state-badge.ts
+++ b/src/components/entity/state-badge.ts
@@ -11,14 +11,11 @@ import {
} from "lit-element";
import { ifDefined } from "lit-html/directives/if-defined";
import { styleMap } from "lit-html/directives/style-map";
-
import { computeActiveState } from "../../common/entity/compute_active_state";
import { computeStateDomain } from "../../common/entity/compute_state_domain";
import { stateIcon } from "../../common/entity/state_icon";
import { iconColorCSS } from "../../common/style/icon_color_css";
-
import type { HomeAssistant } from "../../types";
-
import "../ha-icon";
export class StateBadge extends LitElement {
@@ -40,7 +37,8 @@ export class StateBadge extends LitElement {
protected render(): TemplateResult {
const stateObj = this.stateObj;
- if (!stateObj) {
+ // We either need a `stateObj` or one override
+ if (!stateObj && !this.overrideIcon && !this.overrideImage) {
return html`
`;
@@ -50,7 +48,7 @@ export class StateBadge extends LitElement {
return html``;
}
- const domain = computeStateDomain(stateObj);
+ const domain = stateObj ? computeStateDomain(stateObj) : undefined;
return html`
`;
}
protected updated(changedProps: PropertyValues) {
- if (!changedProps.has("stateObj") || !this.stateObj) {
+ if (
+ !changedProps.has("stateObj") &&
+ !changedProps.has("overrideImage") &&
+ !changedProps.has("overrideIcon")
+ ) {
return;
}
const stateObj = this.stateObj;
@@ -117,7 +119,15 @@ export class StateBadge extends LitElement {
iconStyle.filter = `brightness(${(brightness + 245) / 5}%)`;
}
}
+ } else if (this.overrideImage) {
+ let imageUrl = this.overrideImage;
+ if (this.hass) {
+ imageUrl = this.hass.hassUrl(imageUrl);
+ }
+ hostStyle.backgroundImage = `url(${imageUrl})`;
+ this._showIcon = false;
}
+
this._iconStyle = iconStyle;
Object.assign(this.style, hostStyle);
}
diff --git a/src/components/entity/state-info.ts b/src/components/entity/state-info.ts
index e4d132ab84..d493fb625d 100644
--- a/src/components/entity/state-info.ts
+++ b/src/components/entity/state-info.ts
@@ -1,4 +1,5 @@
import "@polymer/paper-tooltip/paper-tooltip";
+import type { HassEntity } from "home-assistant-js-websocket";
import {
css,
CSSResult,
@@ -8,12 +9,9 @@ import {
property,
TemplateResult,
} from "lit-element";
-import type { HassEntity } from "home-assistant-js-websocket";
-
import { computeStateName } from "../../common/entity/compute_state_name";
import { computeRTL } from "../../common/util/compute_rtl";
import type { HomeAssistant } from "../../types";
-
import "../ha-relative-time";
import "./state-badge";
@@ -25,7 +23,7 @@ class StateInfo extends LitElement {
@property({ type: Boolean }) public inDialog = false;
- // property used only in css
+ // property used only in CSS
@property({ type: Boolean, reflect: true }) public rtl = false;
protected render(): TemplateResult {
diff --git a/src/components/ha-area-picker.ts b/src/components/ha-area-picker.ts
index 9ad7391448..c6b263fd54 100644
--- a/src/components/ha-area-picker.ts
+++ b/src/components/ha-area-picker.ts
@@ -1,5 +1,5 @@
-import "./ha-svg-icon";
import "@material/mwc-icon-button/mwc-icon-button";
+import { mdiClose, mdiMenuDown, mdiMenuUp } from "@mdi/js";
import "@polymer/paper-input/paper-input";
import "@polymer/paper-item/paper-item";
import "@polymer/paper-item/paper-item-body";
@@ -11,27 +11,21 @@ import {
CSSResult,
customElement,
html,
+ internalProperty,
LitElement,
property,
- internalProperty,
- TemplateResult,
PropertyValues,
query,
+ TemplateResult,
} from "lit-element";
+import memoizeOne from "memoize-one";
import { fireEvent } from "../common/dom/fire_event";
+import { computeDomain } from "../common/entity/compute_domain";
import {
AreaRegistryEntry,
createAreaRegistryEntry,
subscribeAreaRegistry,
} from "../data/area_registry";
-import {
- showAlertDialog,
- showPromptDialog,
-} from "../dialogs/generic/show-dialog-box";
-import { SubscribeMixin } from "../mixins/subscribe-mixin";
-import { PolymerChangedEvent } from "../polymer-types";
-import { HomeAssistant } from "../types";
-import memoizeOne from "memoize-one";
import {
DeviceEntityLookup,
DeviceRegistryEntry,
@@ -41,9 +35,15 @@ import {
EntityRegistryEntry,
subscribeEntityRegistry,
} from "../data/entity_registry";
-import { computeDomain } from "../common/entity/compute_domain";
+import {
+ showAlertDialog,
+ showPromptDialog,
+} from "../dialogs/generic/show-dialog-box";
+import { SubscribeMixin } from "../mixins/subscribe-mixin";
+import { PolymerChangedEvent } from "../polymer-types";
+import { HomeAssistant } from "../types";
import type { HaDevicePickerDeviceFilterFunc } from "./device/ha-device-picker";
-import { mdiClose, mdiMenuDown, mdiMenuUp } from "@mdi/js";
+import "./ha-svg-icon";
const rowRenderer = (
root: HTMLElement,
diff --git a/src/components/ha-attributes.ts b/src/components/ha-attributes.ts
index 593d1bcb0e..6d5556cef2 100644
--- a/src/components/ha-attributes.ts
+++ b/src/components/ha-attributes.ts
@@ -12,6 +12,7 @@ import { until } from "lit-html/directives/until";
import hassAttributeUtil, {
formatAttributeName,
} from "../util/hass-attributes-util";
+import { haStyle } from "../resources/styles";
let jsYamlPromise: Promise;
@@ -55,30 +56,36 @@ class HaAttributes extends LitElement {
`;
}
- static get styles(): CSSResult {
- return css`
- .data-entry {
- display: flex;
- flex-direction: row;
- justify-content: space-between;
- }
- .data-entry .value {
- max-width: 50%;
- overflow-wrap: break-word;
- text-align: right;
- }
- .key {
- flex-grow: 1;
- }
- .attribution {
- color: var(--secondary-text-color);
- text-align: center;
- }
- pre {
- font-family: inherit;
- font-size: inherit;
- }
- `;
+ static get styles(): CSSResult[] {
+ return [
+ haStyle,
+ css`
+ .data-entry {
+ display: flex;
+ flex-direction: row;
+ justify-content: space-between;
+ }
+ .data-entry .value {
+ max-width: 60%;
+ overflow-wrap: break-word;
+ text-align: right;
+ }
+ .key {
+ flex-grow: 1;
+ }
+ .attribution {
+ color: var(--secondary-text-color);
+ text-align: center;
+ }
+ pre {
+ font-family: inherit;
+ font-size: inherit;
+ margin: 0px;
+ overflow-wrap: break-word;
+ white-space: pre-line;
+ }
+ `,
+ ];
}
private computeDisplayAttributes(filtersArray: string[]): string[] {
@@ -102,6 +109,7 @@ class HaAttributes extends LitElement {
if (value === null) {
return "-";
}
+ // YAML handling
if (
(Array.isArray(value) && value.some((val) => val instanceof Object)) ||
(!Array.isArray(value) && value instanceof Object)
@@ -112,6 +120,19 @@ class HaAttributes extends LitElement {
const yaml = jsYamlPromise.then((jsYaml) => jsYaml.safeDump(value));
return html` ${until(yaml, "")}
`;
}
+ // URL handling
+ if (typeof value === "string" && value.startsWith("http")) {
+ try {
+ // If invalid URL, exception will be raised
+ const url = new URL(value);
+ if (url.protocol === "http:" || url.protocol === "https:")
+ return html`${value}`;
+ } catch (_) {
+ // Nothing to do here
+ }
+ }
return Array.isArray(value) ? value.join(", ") : value;
}
}
diff --git a/src/components/ha-bar.ts b/src/components/ha-bar.ts
index 6d449c3306..3378958a56 100644
--- a/src/components/ha-bar.ts
+++ b/src/components/ha-bar.ts
@@ -7,7 +7,6 @@ import {
svg,
TemplateResult,
} from "lit-element";
-
import {
getValueInPercentage,
normalize,
diff --git a/src/components/ha-button-toggle-group.ts b/src/components/ha-button-toggle-group.ts
index f932cb4357..33c65cdf82 100644
--- a/src/components/ha-button-toggle-group.ts
+++ b/src/components/ha-button-toggle-group.ts
@@ -1,3 +1,4 @@
+import "@material/mwc-button/mwc-button";
import "@material/mwc-icon-button/mwc-icon-button";
import {
css,
@@ -11,7 +12,6 @@ import {
import { fireEvent } from "../common/dom/fire_event";
import type { ToggleButton } from "../types";
import "./ha-svg-icon";
-import "@material/mwc-button/mwc-button";
@customElement("ha-button-toggle-group")
export class HaButtonToggleGroup extends LitElement {
diff --git a/src/components/ha-camera-stream.ts b/src/components/ha-camera-stream.ts
index 4a8a14e05f..adc3913644 100644
--- a/src/components/ha-camera-stream.ts
+++ b/src/components/ha-camera-stream.ts
@@ -12,6 +12,7 @@ import {
import { fireEvent } from "../common/dom/fire_event";
import { computeStateName } from "../common/entity/compute_state_name";
import { supportsFeature } from "../common/entity/supports-feature";
+import { isComponentLoaded } from "../common/config/is_component_loaded";
import {
CameraEntity,
CAMERA_SUPPORT_STREAM,
@@ -86,7 +87,7 @@ class HaCameraStream extends LitElement {
private get _shouldRenderMJPEG() {
return (
this._forceMJPEG === this.stateObj!.entity_id ||
- !this.hass!.config.components.includes("stream") ||
+ !isComponentLoaded(this.hass!, "stream") ||
!supportsFeature(this.stateObj!, CAMERA_SUPPORT_STREAM)
);
}
diff --git a/src/components/ha-circular-progress.ts b/src/components/ha-circular-progress.ts
index c3e188d267..7b69afa6b8 100644
--- a/src/components/ha-circular-progress.ts
+++ b/src/components/ha-circular-progress.ts
@@ -1,5 +1,5 @@
-import { customElement, property } from "lit-element";
import { CircularProgress } from "@material/mwc-circular-progress";
+import { customElement, property } from "lit-element";
@customElement("ha-circular-progress")
// @ts-ignore
diff --git a/src/components/ha-climate-control.js b/src/components/ha-climate-control.js
index f4732d8623..9962680dfd 100644
--- a/src/components/ha-climate-control.js
+++ b/src/components/ha-climate-control.js
@@ -1,9 +1,9 @@
import "@polymer/iron-flex-layout/iron-flex-layout-classes";
-import "./ha-icon-button";
import { html } from "@polymer/polymer/lib/utils/html-tag";
/* eslint-plugin-disable lit */
import { PolymerElement } from "@polymer/polymer/polymer-element";
import { EventsMixin } from "../mixins/events-mixin";
+import "./ha-icon-button";
/*
* @appliesMixin EventsMixin
diff --git a/src/components/ha-climate-state.ts b/src/components/ha-climate-state.ts
index ba5e5844ec..9b46f6cdb8 100644
--- a/src/components/ha-climate-state.ts
+++ b/src/components/ha-climate-state.ts
@@ -1,3 +1,4 @@
+import { HassEntity } from "home-assistant-js-websocket";
import {
css,
CSSResult,
@@ -7,11 +8,9 @@ import {
property,
TemplateResult,
} from "lit-element";
-import { HassEntity } from "home-assistant-js-websocket";
-
+import { formatNumber } from "../common/string/format_number";
import { CLIMATE_PRESET_NONE } from "../data/climate";
import type { HomeAssistant } from "../types";
-import { formatNumber } from "../common/string/format_number";
@customElement("ha-climate-state")
class HaClimateState extends LitElement {
diff --git a/src/components/ha-code-editor.ts b/src/components/ha-code-editor.ts
index 5dc3b7f826..50e851fd22 100644
--- a/src/components/ha-code-editor.ts
+++ b/src/components/ha-code-editor.ts
@@ -131,81 +131,81 @@ export class HaCodeEditor extends UpdatingElement {
.cm-s-default .CodeMirror-cursor {
border-left: 1px solid var(--secondary-text-color);
}
-
+
.cm-s-default div.CodeMirror-selected, .cm-s-default.CodeMirror-focused div.CodeMirror-selected {
background: rgba(var(--rgb-primary-color), 0.2);
}
-
+
.cm-s-default .CodeMirror-line::selection,
.cm-s-default .CodeMirror-line>span::selection,
.cm-s-default .CodeMirror-line>span>span::selection {
background: rgba(var(--rgb-primary-color), 0.2);
}
-
+
.cm-s-default .cm-keyword {
color: var(--codemirror-keyword, #6262FF);
}
-
+
.cm-s-default .cm-operator {
color: var(--codemirror-operator, #cda869);
}
-
+
.cm-s-default .cm-variable-2 {
color: var(--codemirror-variable-2, #690);
}
-
+
.cm-s-default .cm-builtin {
color: var(--codemirror-builtin, #9B7536);
}
-
+
.cm-s-default .cm-atom {
color: var(--codemirror-atom, #F90);
}
-
+
.cm-s-default .cm-number {
color: var(--codemirror-number, #ca7841);
}
-
+
.cm-s-default .cm-def {
color: var(--codemirror-def, #8DA6CE);
}
-
+
.cm-s-default .cm-string {
color: var(--codemirror-string, #07a);
}
-
+
.cm-s-default .cm-string-2 {
color: var(--codemirror-string-2, #bd6b18);
}
-
+
.cm-s-default .cm-comment {
color: var(--codemirror-comment, #777);
}
-
+
.cm-s-default .cm-variable {
color: var(--codemirror-variable, #07a);
}
-
+
.cm-s-default .cm-tag {
color: var(--codemirror-tag, #997643);
}
-
+
.cm-s-default .cm-meta {
color: var(--codemirror-meta, var(--primary-text-color));
}
-
+
.cm-s-default .cm-attribute {
color: var(--codemirror-attribute, #d6bb6d);
}
-
+
.cm-s-default .cm-property {
color: var(--codemirror-property, #905);
}
-
+
.cm-s-default .cm-qualifier {
color: var(--codemirror-qualifier, #690);
}
-
+
.cm-s-default .cm-variable-3 {
color: var(--codemirror-variable-3, #07a);
}
diff --git a/src/components/ha-combo-box.js b/src/components/ha-combo-box.js
index 62d66b4511..b844d27496 100644
--- a/src/components/ha-combo-box.js
+++ b/src/components/ha-combo-box.js
@@ -1,4 +1,3 @@
-import "./ha-icon-button";
import "@polymer/paper-input/paper-input";
import "@polymer/paper-item/paper-item";
import { html } from "@polymer/polymer/lib/utils/html-tag";
@@ -6,6 +5,7 @@ import { html } from "@polymer/polymer/lib/utils/html-tag";
import { PolymerElement } from "@polymer/polymer/polymer-element";
import "@vaadin/vaadin-combo-box/theme/material/vaadin-combo-box-light";
import { EventsMixin } from "../mixins/events-mixin";
+import "./ha-icon-button";
class HaComboBox extends EventsMixin(PolymerElement) {
static get template() {
diff --git a/src/components/ha-cover-controls.ts b/src/components/ha-cover-controls.ts
index 06def92e23..08910a7735 100644
--- a/src/components/ha-cover-controls.ts
+++ b/src/components/ha-cover-controls.ts
@@ -1,3 +1,4 @@
+import type { HassEntity } from "home-assistant-js-websocket";
import {
css,
CSSResult,
@@ -10,14 +11,11 @@ import {
TemplateResult,
} from "lit-element";
import { classMap } from "lit-html/directives/class-map";
-import type { HassEntity } from "home-assistant-js-websocket";
-
-import type { HomeAssistant } from "../types";
-import { UNAVAILABLE } from "../data/entity";
-import CoverEntity from "../util/cover-model";
-
-import "./ha-icon-button";
import { computeCloseIcon, computeOpenIcon } from "../common/entity/cover_icon";
+import { UNAVAILABLE } from "../data/entity";
+import type { HomeAssistant } from "../types";
+import CoverEntity from "../util/cover-model";
+import "./ha-icon-button";
@customElement("ha-cover-controls")
class HaCoverControls extends LitElement {
diff --git a/src/components/ha-cover-tilt-controls.ts b/src/components/ha-cover-tilt-controls.ts
index 790721f11a..be2f6f4087 100644
--- a/src/components/ha-cover-tilt-controls.ts
+++ b/src/components/ha-cover-tilt-controls.ts
@@ -1,21 +1,19 @@
import { HassEntity } from "home-assistant-js-websocket";
import {
+ css,
+ CSSResult,
+ customElement,
+ html,
+ internalProperty,
LitElement,
property,
- internalProperty,
- CSSResult,
- css,
- customElement,
- TemplateResult,
- html,
PropertyValues,
+ TemplateResult,
} from "lit-element";
import { classMap } from "lit-html/directives/class-map";
-
import { UNAVAILABLE } from "../data/entity";
import { HomeAssistant } from "../types";
import CoverEntity from "../util/cover-model";
-
import "./ha-icon-button";
@customElement("ha-cover-tilt-controls")
@@ -52,7 +50,7 @@ class HaCoverTiltControls extends LitElement {
>
diff --git a/src/components/ha-date-range-picker.ts b/src/components/ha-date-range-picker.ts
index 895a33f984..91db9cab23 100644
--- a/src/components/ha-date-range-picker.ts
+++ b/src/components/ha-date-range-picker.ts
@@ -1,3 +1,9 @@
+import "@material/mwc-button/mwc-button";
+import "@material/mwc-list/mwc-list";
+import { ActionDetail } from "@material/mwc-list/mwc-list-foundation";
+import "@material/mwc-list/mwc-list-item";
+import { mdiCalendar } from "@mdi/js";
+import "@polymer/paper-input/paper-input";
import {
css,
CSSResult,
@@ -5,20 +11,14 @@ import {
html,
LitElement,
property,
- TemplateResult,
PropertyValues,
+ TemplateResult,
} from "lit-element";
-import { HomeAssistant } from "../types";
-import { mdiCalendar } from "@mdi/js";
import { formatDateTime } from "../common/datetime/format_date_time";
-import "@material/mwc-button/mwc-button";
-import "@material/mwc-list/mwc-list-item";
-import "./ha-svg-icon";
-import "@polymer/paper-input/paper-input";
-import "@material/mwc-list/mwc-list";
-import "./date-range-picker";
import { computeRTLDirection } from "../common/util/compute_rtl";
-import { ActionDetail } from "@material/mwc-list/mwc-list-foundation";
+import { HomeAssistant } from "../types";
+import "./date-range-picker";
+import "./ha-svg-icon";
export interface DateRangePickerRanges {
[key: string]: [Date, Date];
diff --git a/src/components/ha-expansion-panel.ts b/src/components/ha-expansion-panel.ts
index f328e3590e..be3dc4f77d 100644
--- a/src/components/ha-expansion-panel.ts
+++ b/src/components/ha-expansion-panel.ts
@@ -1,3 +1,4 @@
+import { mdiChevronDown } from "@mdi/js";
import {
css,
CSSResult,
@@ -8,10 +9,9 @@ import {
query,
TemplateResult,
} from "lit-element";
+import { classMap } from "lit-html/directives/class-map";
import { fireEvent } from "../common/dom/fire_event";
import "./ha-svg-icon";
-import { mdiChevronDown } from "@mdi/js";
-import { classMap } from "lit-html/directives/class-map";
@customElement("ha-expansion-panel")
class HaExpansionPanel extends LitElement {
@@ -74,6 +74,7 @@ class HaExpansionPanel extends LitElement {
var(--divider-color, #e0e0e0)
);
border-radius: var(--ha-card-border-radius, 4px);
+ padding: 0 8px;
}
.summary {
@@ -83,6 +84,7 @@ class HaExpansionPanel extends LitElement {
align-items: center;
cursor: pointer;
overflow: hidden;
+ font-weight: 500;
}
.summary-icon {
diff --git a/src/components/ha-fab.ts b/src/components/ha-fab.ts
index f0917173a0..9725630b6c 100644
--- a/src/components/ha-fab.ts
+++ b/src/components/ha-fab.ts
@@ -1,5 +1,5 @@
-import type { Fab } from "@material/mwc-fab";
import "@material/mwc-fab";
+import type { Fab } from "@material/mwc-fab";
import { customElement } from "lit-element";
import { Constructor } from "../types";
diff --git a/src/components/ha-form/ha-form-constant.ts b/src/components/ha-form/ha-form-constant.ts
index 9ce3a02437..4e6d6e21b4 100644
--- a/src/components/ha-form/ha-form-constant.ts
+++ b/src/components/ha-form/ha-form-constant.ts
@@ -1,15 +1,15 @@
import {
+ css,
+ CSSResult,
customElement,
html,
LitElement,
property,
- TemplateResult,
- CSSResult,
- css,
PropertyValues,
+ TemplateResult,
} from "lit-element";
-import { HaFormElement, HaFormConstantSchema } from "./ha-form";
import { fireEvent } from "../../common/dom/fire_event";
+import { HaFormConstantSchema, HaFormElement } from "./ha-form";
@customElement("ha-form-constant")
export class HaFormConstant extends LitElement implements HaFormElement {
diff --git a/src/components/ha-form/ha-form-multi_select.ts b/src/components/ha-form/ha-form-multi_select.ts
index 29f174a160..290c1a17b0 100644
--- a/src/components/ha-form/ha-form-multi_select.ts
+++ b/src/components/ha-form/ha-form-multi_select.ts
@@ -9,19 +9,19 @@ import {
CSSResult,
customElement,
html,
+ internalProperty,
LitElement,
property,
- internalProperty,
query,
TemplateResult,
} from "lit-element";
import { fireEvent } from "../../common/dom/fire_event";
+import "../ha-icon";
import {
HaFormElement,
HaFormMultiSelectData,
HaFormMultiSelectSchema,
} from "./ha-form";
-import "../ha-icon";
@customElement("ha-form-multi_select")
export class HaFormMultiSelect extends LitElement implements HaFormElement {
diff --git a/src/components/ha-form/ha-form-positive_time_period_dict.ts b/src/components/ha-form/ha-form-positive_time_period_dict.ts
index d5a0db5975..453eaf2a68 100644
--- a/src/components/ha-form/ha-form-positive_time_period_dict.ts
+++ b/src/components/ha-form/ha-form-positive_time_period_dict.ts
@@ -6,8 +6,7 @@ import {
query,
TemplateResult,
} from "lit-element";
-import { fireEvent } from "../../common/dom/fire_event";
-import "../paper-time-input";
+import "../ha-time-input";
import { HaFormElement, HaFormTimeData, HaFormTimeSchema } from "./ha-form";
@customElement("ha-form-positive_time_period_dict")
@@ -20,7 +19,7 @@ export class HaFormTimePeriod extends LitElement implements HaFormElement {
@property() public suffix!: string;
- @query("paper-time-input", true) private _input?: HTMLElement;
+ @query("ha-time-input", true) private _input?: HTMLElement;
public focus() {
if (this._input) {
@@ -30,86 +29,13 @@ export class HaFormTimePeriod extends LitElement implements HaFormElement {
protected render(): TemplateResult {
return html`
-
+ .data=${this.data}
+ >
`;
}
-
- private get _hours() {
- return this.data && this.data.hours ? Number(this.data.hours) : 0;
- }
-
- private get _minutes() {
- return this.data && this.data.minutes ? Number(this.data.minutes) : 0;
- }
-
- private get _seconds() {
- return this.data && this.data.seconds ? Number(this.data.seconds) : 0;
- }
-
- private _parseDuration(value) {
- return value.toString().padStart(2, "0");
- }
-
- private _hourChanged(ev) {
- this._durationChanged(ev, "hours");
- }
-
- private _minChanged(ev) {
- this._durationChanged(ev, "minutes");
- }
-
- private _secChanged(ev) {
- this._durationChanged(ev, "seconds");
- }
-
- private _durationChanged(ev, unit) {
- let value = Number(ev.detail.value);
-
- if (value === this[`_${unit}`]) {
- return;
- }
-
- let hours = this._hours;
- let minutes = this._minutes;
-
- if (unit === "seconds" && value > 59) {
- minutes += Math.floor(value / 60);
- value %= 60;
- }
-
- if (unit === "minutes" && value > 59) {
- hours += Math.floor(value / 60);
- value %= 60;
- }
-
- fireEvent(this, "value-changed", {
- value: {
- hours,
- minutes,
- seconds: this._seconds,
- ...{ [unit]: value },
- },
- });
- }
}
declare global {
diff --git a/src/components/ha-form/ha-form-string.ts b/src/components/ha-form/ha-form-string.ts
index 3f62f9de31..5775051dce 100644
--- a/src/components/ha-form/ha-form-string.ts
+++ b/src/components/ha-form/ha-form-string.ts
@@ -1,16 +1,16 @@
-import "../ha-icon-button";
import "@polymer/paper-input/paper-input";
import type { PaperInputElement } from "@polymer/paper-input/paper-input";
import {
customElement,
html,
+ internalProperty,
LitElement,
property,
- internalProperty,
query,
TemplateResult,
} from "lit-element";
import { fireEvent } from "../../common/dom/fire_event";
+import "../ha-icon-button";
import type {
HaFormElement,
HaFormStringData,
diff --git a/src/components/ha-form/ha-form.ts b/src/components/ha-form/ha-form.ts
index 87af775202..25cf8cd18d 100644
--- a/src/components/ha-form/ha-form.ts
+++ b/src/components/ha-form/ha-form.ts
@@ -8,14 +8,15 @@ import {
} from "lit-element";
import { dynamicElement } from "../../common/dom/dynamic-element-directive";
import { fireEvent } from "../../common/dom/fire_event";
+import { HaTimeData } from "../ha-time-input";
import "./ha-form-boolean";
+import "./ha-form-constant";
import "./ha-form-float";
import "./ha-form-integer";
import "./ha-form-multi_select";
import "./ha-form-positive_time_period_dict";
import "./ha-form-select";
import "./ha-form-string";
-import "./ha-form-constant";
export type HaFormSchema =
| HaFormConstantSchema
@@ -71,7 +72,7 @@ export interface HaFormBooleanSchema extends HaFormBaseSchema {
}
export interface HaFormTimeSchema extends HaFormBaseSchema {
- type: "time";
+ type: "positive_time_period_dict";
}
export interface HaFormDataContainer {
@@ -93,11 +94,7 @@ export type HaFormFloatData = number;
export type HaFormBooleanData = boolean;
export type HaFormSelectData = string;
export type HaFormMultiSelectData = string[];
-export interface HaFormTimeData {
- hours?: number;
- minutes?: number;
- seconds?: number;
-}
+export type HaFormTimeData = HaTimeData;
export interface HaFormElement extends LitElement {
schema: HaFormSchema | HaFormSchema[];
diff --git a/src/components/ha-gauge.ts b/src/components/ha-gauge.ts
index 249a5639a5..692189c243 100644
--- a/src/components/ha-gauge.ts
+++ b/src/components/ha-gauge.ts
@@ -1,18 +1,17 @@
import {
- LitElement,
- svg,
- customElement,
css,
- property,
+ customElement,
internalProperty,
+ LitElement,
+ property,
PropertyValues,
+ svg,
} from "lit-element";
-import { styleMap } from "lit-html/directives/style-map";
-import { afterNextRender } from "../common/util/render-status";
import { ifDefined } from "lit-html/directives/if-defined";
-
-import { getValueInPercentage, normalize } from "../util/calculate";
+import { styleMap } from "lit-html/directives/style-map";
import { formatNumber } from "../common/string/format_number";
+import { afterNextRender } from "../common/util/render-status";
+import { getValueInPercentage, normalize } from "../util/calculate";
const getAngle = (value: number, min: number, max: number) => {
const percentage = getValueInPercentage(normalize(value, min, max), min, max);
diff --git a/src/components/ha-icon-button-arrow-next.ts b/src/components/ha-icon-button-arrow-next.ts
index fa2917db6a..85bc2d245f 100644
--- a/src/components/ha-icon-button-arrow-next.ts
+++ b/src/components/ha-icon-button-arrow-next.ts
@@ -1,15 +1,15 @@
+import "@material/mwc-icon-button/mwc-icon-button";
+import { mdiArrowLeft, mdiArrowRight } from "@mdi/js";
import {
+ customElement,
+ html,
+ internalProperty,
LitElement,
property,
- internalProperty,
TemplateResult,
- html,
- customElement,
} from "lit-element";
-import { mdiArrowLeft, mdiArrowRight } from "@mdi/js";
-import "@material/mwc-icon-button/mwc-icon-button";
-import "./ha-svg-icon";
import { HomeAssistant } from "../types";
+import "./ha-svg-icon";
@customElement("ha-icon-button-arrow-next")
export class HaIconButtonArrowNext extends LitElement {
diff --git a/src/components/ha-icon-button-arrow-prev.ts b/src/components/ha-icon-button-arrow-prev.ts
index c7fbada10d..c426c589a7 100644
--- a/src/components/ha-icon-button-arrow-prev.ts
+++ b/src/components/ha-icon-button-arrow-prev.ts
@@ -1,15 +1,15 @@
+import "@material/mwc-icon-button/mwc-icon-button";
+import { mdiArrowLeft, mdiArrowRight } from "@mdi/js";
import {
+ customElement,
+ html,
+ internalProperty,
LitElement,
property,
- internalProperty,
TemplateResult,
- html,
- customElement,
} from "lit-element";
-import { mdiArrowLeft, mdiArrowRight } from "@mdi/js";
-import "@material/mwc-icon-button/mwc-icon-button";
-import "./ha-svg-icon";
import { HomeAssistant } from "../types";
+import "./ha-svg-icon";
@customElement("ha-icon-button-arrow-prev")
export class HaIconButtonArrowPrev extends LitElement {
diff --git a/src/components/ha-icon-button-next.ts b/src/components/ha-icon-button-next.ts
index 48eca31a4a..04213393eb 100644
--- a/src/components/ha-icon-button-next.ts
+++ b/src/components/ha-icon-button-next.ts
@@ -1,15 +1,15 @@
+import "@material/mwc-icon-button";
+import { mdiChevronLeft, mdiChevronRight } from "@mdi/js";
import {
+ customElement,
+ html,
+ internalProperty,
LitElement,
property,
- internalProperty,
TemplateResult,
- html,
- customElement,
} from "lit-element";
-import { mdiChevronRight, mdiChevronLeft } from "@mdi/js";
-import "@material/mwc-icon-button";
-import "./ha-svg-icon";
import { HomeAssistant } from "../types";
+import "./ha-svg-icon";
@customElement("ha-icon-button-next")
export class HaIconButtonNext extends LitElement {
diff --git a/src/components/ha-icon-button-prev.ts b/src/components/ha-icon-button-prev.ts
index 8b88afedda..6384039f13 100644
--- a/src/components/ha-icon-button-prev.ts
+++ b/src/components/ha-icon-button-prev.ts
@@ -1,15 +1,15 @@
+import "@material/mwc-icon-button/mwc-icon-button";
+import { mdiChevronLeft, mdiChevronRight } from "@mdi/js";
import {
+ customElement,
+ html,
+ internalProperty,
LitElement,
property,
- internalProperty,
TemplateResult,
- html,
- customElement,
} from "lit-element";
-import { mdiChevronRight, mdiChevronLeft } from "@mdi/js";
-import "@material/mwc-icon-button/mwc-icon-button";
-import "./ha-svg-icon";
import { HomeAssistant } from "../types";
+import "./ha-svg-icon";
@customElement("ha-icon-button-prev")
export class HaIconButtonPrev extends LitElement {
diff --git a/src/components/ha-icon-button.ts b/src/components/ha-icon-button.ts
index 1c645b50cd..e8cc62ab55 100644
--- a/src/components/ha-icon-button.ts
+++ b/src/components/ha-icon-button.ts
@@ -1,12 +1,12 @@
import "@material/mwc-icon-button";
import {
+ css,
+ CSSResult,
customElement,
html,
- TemplateResult,
- property,
LitElement,
- CSSResult,
- css,
+ property,
+ TemplateResult,
} from "lit-element";
import "./ha-icon";
diff --git a/src/components/ha-icon-next.ts b/src/components/ha-icon-next.ts
index 742f342e91..6b6eef9b01 100644
--- a/src/components/ha-icon-next.ts
+++ b/src/components/ha-icon-next.ts
@@ -1,5 +1,5 @@
+import { mdiChevronLeft, mdiChevronRight } from "@mdi/js";
import { HaSvgIcon } from "./ha-svg-icon";
-import { mdiChevronRight, mdiChevronLeft } from "@mdi/js";
export class HaIconNext extends HaSvgIcon {
public connectedCallback() {
diff --git a/src/components/ha-icon-prev.ts b/src/components/ha-icon-prev.ts
index df800fd52d..29712fc89b 100644
--- a/src/components/ha-icon-prev.ts
+++ b/src/components/ha-icon-prev.ts
@@ -1,5 +1,5 @@
+import { mdiChevronLeft, mdiChevronRight } from "@mdi/js";
import { HaSvgIcon } from "./ha-svg-icon";
-import { mdiChevronRight, mdiChevronLeft } from "@mdi/js";
export class HaIconPrev extends HaSvgIcon {
public connectedCallback() {
diff --git a/src/components/ha-icon.ts b/src/components/ha-icon.ts
index 8fe065bc6e..2cb7e0bdf6 100644
--- a/src/components/ha-icon.ts
+++ b/src/components/ha-icon.ts
@@ -1,28 +1,28 @@
import "@polymer/iron-icon/iron-icon";
import {
- customElement,
- LitElement,
- property,
- internalProperty,
- PropertyValues,
- html,
- TemplateResult,
css,
CSSResult,
+ customElement,
+ html,
+ internalProperty,
+ LitElement,
+ property,
+ PropertyValues,
+ TemplateResult,
} from "lit-element";
-import "./ha-svg-icon";
-import { customIconsets, CustomIcon } from "../data/custom_iconsets";
+import { fireEvent } from "../common/dom/fire_event";
+import { debounce } from "../common/util/debounce";
+import { CustomIcon, customIconsets } from "../data/custom_iconsets";
import {
- Chunks,
- MDI_PREFIXES,
- getIcon,
- findIconChunk,
- Icons,
checkCacheVersion,
+ Chunks,
+ findIconChunk,
+ getIcon,
+ Icons,
+ MDI_PREFIXES,
writeCache,
} from "../data/iconsets";
-import { debounce } from "../common/util/debounce";
-import { fireEvent } from "../common/dom/fire_event";
+import "./ha-svg-icon";
interface DeprecatedIcon {
[key: string]: {
diff --git a/src/components/ha-markdown.ts b/src/components/ha-markdown.ts
index 484776a2b0..c98f5d07a9 100644
--- a/src/components/ha-markdown.ts
+++ b/src/components/ha-markdown.ts
@@ -7,7 +7,6 @@ import {
property,
TemplateResult,
} from "lit-element";
-
import "./ha-markdown-element";
@customElement("ha-markdown")
diff --git a/src/components/ha-push-notifications-toggle.js b/src/components/ha-push-notifications-toggle.js
index b8bead07fc..dfb876b9a7 100644
--- a/src/components/ha-push-notifications-toggle.js
+++ b/src/components/ha-push-notifications-toggle.js
@@ -2,8 +2,8 @@ import { html } from "@polymer/polymer/lib/utils/html-tag";
/* eslint-plugin-disable lit */
import { PolymerElement } from "@polymer/polymer/polymer-element";
import { getAppKey } from "../data/notify_html5";
-import { EventsMixin } from "../mixins/events-mixin";
import { showPromptDialog } from "../dialogs/generic/show-dialog-box";
+import { EventsMixin } from "../mixins/events-mixin";
import "./ha-switch";
export const pushSupported =
diff --git a/src/components/ha-related-items.ts b/src/components/ha-related-items.ts
index 4bdcdda1e9..3d2cc72c27 100644
--- a/src/components/ha-related-items.ts
+++ b/src/components/ha-related-items.ts
@@ -4,9 +4,9 @@ import {
CSSResult,
customElement,
html,
+ internalProperty,
LitElement,
property,
- internalProperty,
PropertyValues,
TemplateResult,
} from "lit-element";
diff --git a/src/components/ha-relative-time.ts b/src/components/ha-relative-time.ts
index a2f9e403ca..98b9228dec 100644
--- a/src/components/ha-relative-time.ts
+++ b/src/components/ha-relative-time.ts
@@ -1,12 +1,10 @@
import {
customElement,
- UpdatingElement,
property,
PropertyValues,
+ UpdatingElement,
} from "lit-element";
-
import relativeTime from "../common/datetime/relative_time";
-
import type { HomeAssistant } from "../types";
@customElement("ha-relative-time")
diff --git a/src/components/ha-selector/ha-selector-action.ts b/src/components/ha-selector/ha-selector-action.ts
index c6e06e3a12..be91204c7f 100644
--- a/src/components/ha-selector/ha-selector-action.ts
+++ b/src/components/ha-selector/ha-selector-action.ts
@@ -6,10 +6,10 @@ import {
LitElement,
property,
} from "lit-element";
-import { HomeAssistant } from "../../types";
-import { ActionSelector } from "../../data/selector";
import { Action } from "../../data/script";
+import { ActionSelector } from "../../data/selector";
import "../../panels/config/automation/action/ha-automation-action";
+import { HomeAssistant } from "../../types";
@customElement("ha-selector-action")
export class HaActionSelector extends LitElement {
diff --git a/src/components/ha-selector/ha-selector-area.ts b/src/components/ha-selector/ha-selector-area.ts
index 903626440b..8023dc4844 100644
--- a/src/components/ha-selector/ha-selector-area.ts
+++ b/src/components/ha-selector/ha-selector-area.ts
@@ -5,12 +5,12 @@ import {
LitElement,
property,
} from "lit-element";
-import { HomeAssistant } from "../../types";
-import { AreaSelector } from "../../data/selector";
-import "../ha-area-picker";
import { ConfigEntry, getConfigEntries } from "../../data/config_entries";
import { DeviceRegistryEntry } from "../../data/device_registry";
import { EntityRegistryEntry } from "../../data/entity_registry";
+import { AreaSelector } from "../../data/selector";
+import { HomeAssistant } from "../../types";
+import "../ha-area-picker";
@customElement("ha-selector-area")
export class HaAreaSelector extends LitElement {
diff --git a/src/components/ha-selector/ha-selector-device.ts b/src/components/ha-selector/ha-selector-device.ts
index f3f2c1ff35..d9ec80655c 100644
--- a/src/components/ha-selector/ha-selector-device.ts
+++ b/src/components/ha-selector/ha-selector-device.ts
@@ -5,11 +5,11 @@ import {
LitElement,
property,
} from "lit-element";
+import { ConfigEntry, getConfigEntries } from "../../data/config_entries";
+import { DeviceRegistryEntry } from "../../data/device_registry";
+import { DeviceSelector } from "../../data/selector";
import { HomeAssistant } from "../../types";
import "../device/ha-device-picker";
-import { DeviceRegistryEntry } from "../../data/device_registry";
-import { ConfigEntry, getConfigEntries } from "../../data/config_entries";
-import { DeviceSelector } from "../../data/selector";
@customElement("ha-selector-device")
export class HaDeviceSelector extends LitElement {
diff --git a/src/components/ha-selector/ha-selector-entity.ts b/src/components/ha-selector/ha-selector-entity.ts
index e6bfba75b5..78c7003e1f 100644
--- a/src/components/ha-selector/ha-selector-entity.ts
+++ b/src/components/ha-selector/ha-selector-entity.ts
@@ -1,3 +1,4 @@
+import { HassEntity, UnsubscribeFunc } from "home-assistant-js-websocket";
import {
customElement,
html,
@@ -5,13 +6,12 @@ import {
LitElement,
property,
} from "lit-element";
-import { HomeAssistant } from "../../types";
-import "../entity/ha-entity-picker";
-import { HassEntity, UnsubscribeFunc } from "home-assistant-js-websocket";
import { computeStateDomain } from "../../common/entity/compute_state_domain";
import { subscribeEntityRegistry } from "../../data/entity_registry";
-import { SubscribeMixin } from "../../mixins/subscribe-mixin";
import { EntitySelector } from "../../data/selector";
+import { SubscribeMixin } from "../../mixins/subscribe-mixin";
+import { HomeAssistant } from "../../types";
+import "../entity/ha-entity-picker";
@customElement("ha-selector-entity")
export class HaEntitySelector extends SubscribeMixin(LitElement) {
diff --git a/src/components/ha-selector/ha-selector-number.ts b/src/components/ha-selector/ha-selector-number.ts
index 15d1ff0e23..3a819cf9c5 100644
--- a/src/components/ha-selector/ha-selector-number.ts
+++ b/src/components/ha-selector/ha-selector-number.ts
@@ -1,3 +1,4 @@
+import "@polymer/paper-input/paper-input";
import {
css,
CSSResult,
@@ -6,12 +7,11 @@ import {
LitElement,
property,
} from "lit-element";
-import { HomeAssistant } from "../../types";
-import { NumberSelector } from "../../data/selector";
-import "@polymer/paper-input/paper-input";
-import "../ha-slider";
-import { fireEvent } from "../../common/dom/fire_event";
import { classMap } from "lit-html/directives/class-map";
+import { fireEvent } from "../../common/dom/fire_event";
+import { NumberSelector } from "../../data/selector";
+import { HomeAssistant } from "../../types";
+import "../ha-slider";
@customElement("ha-selector-number")
export class HaNumberSelector extends LitElement {
diff --git a/src/components/ha-selector/ha-selector-object.ts b/src/components/ha-selector/ha-selector-object.ts
new file mode 100644
index 0000000000..29159e3e8f
--- /dev/null
+++ b/src/components/ha-selector/ha-selector-object.ts
@@ -0,0 +1,37 @@
+import { customElement, html, LitElement, property } from "lit-element";
+import { fireEvent } from "../../common/dom/fire_event";
+import { HomeAssistant } from "../../types";
+import "../ha-yaml-editor";
+
+@customElement("ha-selector-object")
+export class HaObjectSelector extends LitElement {
+ @property() public hass!: HomeAssistant;
+
+ @property() public value?: any;
+
+ @property() public label?: string;
+
+ protected render() {
+ return html``;
+ }
+
+ private _handleChange(ev) {
+ const value = ev.target.value;
+ if (!ev.target.isValid) {
+ return;
+ }
+ if (this.value === value) {
+ return;
+ }
+ fireEvent(this, "value-changed", { value });
+ }
+}
+
+declare global {
+ interface HTMLElementTagNameMap {
+ "ha-selector-object": HaObjectSelector;
+ }
+}
diff --git a/src/components/ha-selector/ha-selector-target.ts b/src/components/ha-selector/ha-selector-target.ts
index aa6d2cfdb5..23c383e647 100644
--- a/src/components/ha-selector/ha-selector-target.ts
+++ b/src/components/ha-selector/ha-selector-target.ts
@@ -1,3 +1,9 @@
+import "@material/mwc-list/mwc-list";
+import "@material/mwc-list/mwc-list-item";
+import "@material/mwc-tab-bar/mwc-tab-bar";
+import "@material/mwc-tab/mwc-tab";
+import "@polymer/paper-input/paper-input";
+import { HassEntity, UnsubscribeFunc } from "home-assistant-js-websocket";
import {
css,
CSSResult,
@@ -7,23 +13,17 @@ import {
LitElement,
property,
} from "lit-element";
-import { HomeAssistant } from "../../types";
-import { TargetSelector } from "../../data/selector";
import { ConfigEntry, getConfigEntries } from "../../data/config_entries";
import { DeviceRegistryEntry } from "../../data/device_registry";
-import "../ha-target-picker";
-import "@material/mwc-list/mwc-list-item";
-import "@polymer/paper-input/paper-input";
-import "@material/mwc-list/mwc-list";
import {
EntityRegistryEntry,
subscribeEntityRegistry,
} from "../../data/entity_registry";
+import { TargetSelector } from "../../data/selector";
import { Target } from "../../data/target";
-import "@material/mwc-tab-bar/mwc-tab-bar";
-import "@material/mwc-tab/mwc-tab";
-import { HassEntity, UnsubscribeFunc } from "home-assistant-js-websocket";
import { SubscribeMixin } from "../../mixins/subscribe-mixin";
+import { HomeAssistant } from "../../types";
+import "../ha-target-picker";
@customElement("ha-selector-target")
export class HaTargetSelector extends SubscribeMixin(LitElement) {
diff --git a/src/components/ha-selector/ha-selector-text.ts b/src/components/ha-selector/ha-selector-text.ts
new file mode 100644
index 0000000000..32fa638ff0
--- /dev/null
+++ b/src/components/ha-selector/ha-selector-text.ts
@@ -0,0 +1,50 @@
+import { customElement, html, LitElement, property } from "lit-element";
+import { fireEvent } from "../../common/dom/fire_event";
+import { HomeAssistant } from "../../types";
+import "@polymer/paper-input/paper-textarea";
+import "@polymer/paper-input/paper-input";
+import { StringSelector } from "../../data/selector";
+
+@customElement("ha-selector-text")
+export class HaTextSelector extends LitElement {
+ @property() public hass!: HomeAssistant;
+
+ @property() public value?: any;
+
+ @property() public label?: string;
+
+ @property() public selector!: StringSelector;
+
+ protected render() {
+ if (this.selector.text?.multiline) {
+ return html``;
+ }
+ return html``;
+ }
+
+ private _handleChange(ev) {
+ const value = ev.target.value;
+ if (this.value === value) {
+ return;
+ }
+ fireEvent(this, "value-changed", { value });
+ }
+}
+
+declare global {
+ interface HTMLElementTagNameMap {
+ "ha-selector-text": HaTextSelector;
+ }
+}
diff --git a/src/components/ha-selector/ha-selector-time.ts b/src/components/ha-selector/ha-selector-time.ts
index 8bfa3d1ec1..688b23dad3 100644
--- a/src/components/ha-selector/ha-selector-time.ts
+++ b/src/components/ha-selector/ha-selector-time.ts
@@ -1,7 +1,7 @@
import { customElement, html, LitElement, property } from "lit-element";
-import { HomeAssistant } from "../../types";
-import { TimeSelector } from "../../data/selector";
import { fireEvent } from "../../common/dom/fire_event";
+import { TimeSelector } from "../../data/selector";
+import { HomeAssistant } from "../../types";
import "../paper-time-input";
const test = new Date().toLocaleString();
diff --git a/src/components/ha-selector/ha-selector.ts b/src/components/ha-selector/ha-selector.ts
index 3c88b2da25..88d702c9d1 100644
--- a/src/components/ha-selector/ha-selector.ts
+++ b/src/components/ha-selector/ha-selector.ts
@@ -1,16 +1,17 @@
import { customElement, html, LitElement, property } from "lit-element";
import { dynamicElement } from "../../common/dom/dynamic-element-directive";
-import { HomeAssistant } from "../../types";
-
-import "./ha-selector-entity";
-import "./ha-selector-device";
-import "./ha-selector-area";
-import "./ha-selector-target";
-import "./ha-selector-number";
-import "./ha-selector-boolean";
-import "./ha-selector-time";
-import "./ha-selector-action";
import { Selector } from "../../data/selector";
+import { HomeAssistant } from "../../types";
+import "./ha-selector-action";
+import "./ha-selector-area";
+import "./ha-selector-boolean";
+import "./ha-selector-device";
+import "./ha-selector-entity";
+import "./ha-selector-number";
+import "./ha-selector-target";
+import "./ha-selector-time";
+import "./ha-selector-object";
+import "./ha-selector-text";
@customElement("ha-selector")
export class HaSelector extends LitElement {
diff --git a/src/components/ha-sidebar.ts b/src/components/ha-sidebar.ts
index 7a4791b967..ffc5d57090 100644
--- a/src/components/ha-sidebar.ts
+++ b/src/components/ha-sidebar.ts
@@ -772,9 +772,12 @@ class HaSidebar extends LitElement {
border-bottom: 1px solid transparent;
white-space: nowrap;
font-weight: 400;
- color: var(--primary-text-color);
+ color: var(--sidebar-menu-button-text-color, --primary-text-color);
border-bottom: 1px solid var(--divider-color);
- background-color: var(--primary-background-color);
+ background-color: var(
+ --sidebar-menu-button-background-color,
+ --primary-background-color
+ );
font-size: 20px;
align-items: center;
padding-left: calc(4px + env(safe-area-inset-left));
diff --git a/src/components/ha-svg-icon.ts b/src/components/ha-svg-icon.ts
index 6e58ea5378..ecc95d0d44 100644
--- a/src/components/ha-svg-icon.ts
+++ b/src/components/ha-svg-icon.ts
@@ -16,7 +16,7 @@ export class HaSvgIcon extends LitElement {
protected render(): SVGTemplateResult {
return svg`
-