mirror of
https://github.com/home-assistant/frontend.git
synced 2025-07-26 02:36:37 +00:00
Migrate state cards from Polymer to Lit (#18257)
This commit is contained in:
parent
f4ee734ea3
commit
4582c3ba0a
@ -23,7 +23,7 @@ class DemoMoreInfo extends LitElement {
|
|||||||
<state-card-content
|
<state-card-content
|
||||||
.stateObj=${state}
|
.stateObj=${state}
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
in-dialog
|
inDialog
|
||||||
></state-card-content>
|
></state-card-content>
|
||||||
|
|
||||||
<more-info-content
|
<more-info-content
|
||||||
|
3
gallery/src/pages/more-info/input-text.markdown
Normal file
3
gallery/src/pages/more-info/input-text.markdown
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
---
|
||||||
|
title: Input Text
|
||||||
|
---
|
46
gallery/src/pages/more-info/input-text.ts
Normal file
46
gallery/src/pages/more-info/input-text.ts
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
import { html, LitElement, PropertyValues, TemplateResult } from "lit";
|
||||||
|
import { customElement, property, query } from "lit/decorators";
|
||||||
|
import "../../../../src/components/ha-card";
|
||||||
|
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("input_text", "text", "Inspiration", {
|
||||||
|
friendly_name: "Text",
|
||||||
|
mode: "text",
|
||||||
|
}),
|
||||||
|
];
|
||||||
|
|
||||||
|
@customElement("demo-more-info-input-text")
|
||||||
|
class DemoMoreInfoInputText extends LitElement {
|
||||||
|
@property() public hass!: MockHomeAssistant;
|
||||||
|
|
||||||
|
@query("demo-more-infos") private _demoRoot!: HTMLElement;
|
||||||
|
|
||||||
|
protected render(): TemplateResult {
|
||||||
|
return html`
|
||||||
|
<demo-more-infos
|
||||||
|
.hass=${this.hass}
|
||||||
|
.entities=${ENTITIES.map((ent) => ent.entityId)}
|
||||||
|
></demo-more-infos>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected firstUpdated(changedProperties: PropertyValues) {
|
||||||
|
super.firstUpdated(changedProperties);
|
||||||
|
const hass = provideHass(this._demoRoot);
|
||||||
|
hass.updateTranslations(null, "en");
|
||||||
|
hass.addEntities(ENTITIES);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface HTMLElementTagNameMap {
|
||||||
|
"demo-more-info-input-text": DemoMoreInfoInputText;
|
||||||
|
}
|
||||||
|
}
|
3
gallery/src/pages/more-info/lock.markdown
Normal file
3
gallery/src/pages/more-info/lock.markdown
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
---
|
||||||
|
title: Lock
|
||||||
|
---
|
49
gallery/src/pages/more-info/lock.ts
Normal file
49
gallery/src/pages/more-info/lock.ts
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
import { html, LitElement, PropertyValues, TemplateResult } from "lit";
|
||||||
|
import { customElement, property, query } from "lit/decorators";
|
||||||
|
import "../../../../src/components/ha-card";
|
||||||
|
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("lock", "lock", "locked", {
|
||||||
|
friendly_name: "Lock",
|
||||||
|
device_class: "lock",
|
||||||
|
}),
|
||||||
|
getEntity("lock", "unavailable", "unavailable", {
|
||||||
|
friendly_name: "Unavailable lock",
|
||||||
|
}),
|
||||||
|
];
|
||||||
|
|
||||||
|
@customElement("demo-more-info-lock")
|
||||||
|
class DemoMoreInfoLock extends LitElement {
|
||||||
|
@property() public hass!: MockHomeAssistant;
|
||||||
|
|
||||||
|
@query("demo-more-infos") private _demoRoot!: HTMLElement;
|
||||||
|
|
||||||
|
protected render(): TemplateResult {
|
||||||
|
return html`
|
||||||
|
<demo-more-infos
|
||||||
|
.hass=${this.hass}
|
||||||
|
.entities=${ENTITIES.map((ent) => ent.entityId)}
|
||||||
|
></demo-more-infos>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected firstUpdated(changedProperties: PropertyValues) {
|
||||||
|
super.firstUpdated(changedProperties);
|
||||||
|
const hass = provideHass(this._demoRoot);
|
||||||
|
hass.updateTranslations(null, "en");
|
||||||
|
hass.addEntities(ENTITIES);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface HTMLElementTagNameMap {
|
||||||
|
"demo-more-info-lock": DemoMoreInfoLock;
|
||||||
|
}
|
||||||
|
}
|
3
gallery/src/pages/more-info/media-player.markdown
Normal file
3
gallery/src/pages/more-info/media-player.markdown
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
---
|
||||||
|
title: Media Player
|
||||||
|
---
|
41
gallery/src/pages/more-info/media-player.ts
Normal file
41
gallery/src/pages/more-info/media-player.ts
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
import { html, LitElement, PropertyValues, TemplateResult } from "lit";
|
||||||
|
import { customElement, property, query } from "lit/decorators";
|
||||||
|
import "../../../../src/components/ha-card";
|
||||||
|
import "../../../../src/dialogs/more-info/more-info-content";
|
||||||
|
import {
|
||||||
|
MockHomeAssistant,
|
||||||
|
provideHass,
|
||||||
|
} from "../../../../src/fake_data/provide_hass";
|
||||||
|
import "../../components/demo-more-infos";
|
||||||
|
import { createMediaPlayerEntities } from "../../data/media_players";
|
||||||
|
|
||||||
|
const ENTITIES = createMediaPlayerEntities();
|
||||||
|
|
||||||
|
@customElement("demo-more-info-media-player")
|
||||||
|
class DemoMoreInfoMediaPlayer extends LitElement {
|
||||||
|
@property() public hass!: MockHomeAssistant;
|
||||||
|
|
||||||
|
@query("demo-more-infos") private _demoRoot!: HTMLElement;
|
||||||
|
|
||||||
|
protected render(): TemplateResult {
|
||||||
|
return html`
|
||||||
|
<demo-more-infos
|
||||||
|
.hass=${this.hass}
|
||||||
|
.entities=${ENTITIES.map((ent) => ent.entityId)}
|
||||||
|
></demo-more-infos>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected firstUpdated(changedProperties: PropertyValues) {
|
||||||
|
super.firstUpdated(changedProperties);
|
||||||
|
const hass = provideHass(this._demoRoot);
|
||||||
|
hass.updateTranslations(null, "en");
|
||||||
|
hass.addEntities(ENTITIES);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface HTMLElementTagNameMap {
|
||||||
|
"demo-more-info-media-player": DemoMoreInfoMediaPlayer;
|
||||||
|
}
|
||||||
|
}
|
3
gallery/src/pages/more-info/number.markdown
Normal file
3
gallery/src/pages/more-info/number.markdown
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
---
|
||||||
|
title: Number
|
||||||
|
---
|
78
gallery/src/pages/more-info/number.ts
Normal file
78
gallery/src/pages/more-info/number.ts
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
import { html, LitElement, PropertyValues, TemplateResult } from "lit";
|
||||||
|
import { customElement, property, query } from "lit/decorators";
|
||||||
|
import "../../../../src/components/ha-card";
|
||||||
|
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("number", "box1", 0, {
|
||||||
|
friendly_name: "Box1",
|
||||||
|
min: 0,
|
||||||
|
max: 100,
|
||||||
|
step: 1,
|
||||||
|
initial: 0,
|
||||||
|
mode: "box",
|
||||||
|
unit_of_measurement: "items",
|
||||||
|
}),
|
||||||
|
getEntity("number", "slider1", 0, {
|
||||||
|
friendly_name: "Slider1",
|
||||||
|
min: 0,
|
||||||
|
max: 100,
|
||||||
|
step: 1,
|
||||||
|
initial: 0,
|
||||||
|
mode: "slider",
|
||||||
|
unit_of_measurement: "items",
|
||||||
|
}),
|
||||||
|
getEntity("number", "auto1", 0, {
|
||||||
|
friendly_name: "Auto1",
|
||||||
|
min: 0,
|
||||||
|
max: 1000,
|
||||||
|
step: 1,
|
||||||
|
initial: 0,
|
||||||
|
mode: "auto",
|
||||||
|
unit_of_measurement: "items",
|
||||||
|
}),
|
||||||
|
getEntity("number", "auto2", 0, {
|
||||||
|
friendly_name: "Auto2",
|
||||||
|
min: 0,
|
||||||
|
max: 100,
|
||||||
|
step: 1,
|
||||||
|
initial: 0,
|
||||||
|
mode: "auto",
|
||||||
|
unit_of_measurement: "items",
|
||||||
|
}),
|
||||||
|
];
|
||||||
|
|
||||||
|
@customElement("demo-more-info-number")
|
||||||
|
class DemoMoreInfoNumber extends LitElement {
|
||||||
|
@property() public hass!: MockHomeAssistant;
|
||||||
|
|
||||||
|
@query("demo-more-infos") private _demoRoot!: HTMLElement;
|
||||||
|
|
||||||
|
protected render(): TemplateResult {
|
||||||
|
return html`
|
||||||
|
<demo-more-infos
|
||||||
|
.hass=${this.hass}
|
||||||
|
.entities=${ENTITIES.map((ent) => ent.entityId)}
|
||||||
|
></demo-more-infos>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected firstUpdated(changedProperties: PropertyValues) {
|
||||||
|
super.firstUpdated(changedProperties);
|
||||||
|
const hass = provideHass(this._demoRoot);
|
||||||
|
hass.updateTranslations(null, "en");
|
||||||
|
hass.addEntities(ENTITIES);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface HTMLElementTagNameMap {
|
||||||
|
"demo-more-info-number": DemoMoreInfoNumber;
|
||||||
|
}
|
||||||
|
}
|
3
gallery/src/pages/more-info/scene.markdown
Normal file
3
gallery/src/pages/more-info/scene.markdown
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
---
|
||||||
|
title: Scene
|
||||||
|
---
|
49
gallery/src/pages/more-info/scene.ts
Normal file
49
gallery/src/pages/more-info/scene.ts
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
import { html, LitElement, PropertyValues, TemplateResult } from "lit";
|
||||||
|
import { customElement, property, query } from "lit/decorators";
|
||||||
|
import "../../../../src/components/ha-card";
|
||||||
|
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("scene", "romantic_lights", "scening", {
|
||||||
|
entity_id: ["light.bed_light", "light.ceiling_lights"],
|
||||||
|
friendly_name: "Romantic Scene",
|
||||||
|
}),
|
||||||
|
getEntity("scene", "unavailable", "unavailable", {
|
||||||
|
friendly_name: "Romantic Scene",
|
||||||
|
}),
|
||||||
|
];
|
||||||
|
|
||||||
|
@customElement("demo-more-info-scene")
|
||||||
|
class DemoMoreInfoScene extends LitElement {
|
||||||
|
@property() public hass!: MockHomeAssistant;
|
||||||
|
|
||||||
|
@query("demo-more-infos") private _demoRoot!: HTMLElement;
|
||||||
|
|
||||||
|
protected render(): TemplateResult {
|
||||||
|
return html`
|
||||||
|
<demo-more-infos
|
||||||
|
.hass=${this.hass}
|
||||||
|
.entities=${ENTITIES.map((ent) => ent.entityId)}
|
||||||
|
></demo-more-infos>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected firstUpdated(changedProperties: PropertyValues) {
|
||||||
|
super.firstUpdated(changedProperties);
|
||||||
|
const hass = provideHass(this._demoRoot);
|
||||||
|
hass.updateTranslations(null, "en");
|
||||||
|
hass.addEntities(ENTITIES);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface HTMLElementTagNameMap {
|
||||||
|
"demo-more-info-scene": DemoMoreInfoScene;
|
||||||
|
}
|
||||||
|
}
|
3
gallery/src/pages/more-info/timer.markdown
Normal file
3
gallery/src/pages/more-info/timer.markdown
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
---
|
||||||
|
title: Timer
|
||||||
|
---
|
46
gallery/src/pages/more-info/timer.ts
Normal file
46
gallery/src/pages/more-info/timer.ts
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
import { html, LitElement, PropertyValues, TemplateResult } from "lit";
|
||||||
|
import { customElement, property, query } from "lit/decorators";
|
||||||
|
import "../../../../src/components/ha-card";
|
||||||
|
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("timer", "timer", "idle", {
|
||||||
|
friendly_name: "Timer",
|
||||||
|
duration: "0:05:00",
|
||||||
|
}),
|
||||||
|
];
|
||||||
|
|
||||||
|
@customElement("demo-more-info-timer")
|
||||||
|
class DemoMoreInfoTimer extends LitElement {
|
||||||
|
@property() public hass!: MockHomeAssistant;
|
||||||
|
|
||||||
|
@query("demo-more-infos") private _demoRoot!: HTMLElement;
|
||||||
|
|
||||||
|
protected render(): TemplateResult {
|
||||||
|
return html`
|
||||||
|
<demo-more-infos
|
||||||
|
.hass=${this.hass}
|
||||||
|
.entities=${ENTITIES.map((ent) => ent.entityId)}
|
||||||
|
></demo-more-infos>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected firstUpdated(changedProperties: PropertyValues) {
|
||||||
|
super.firstUpdated(changedProperties);
|
||||||
|
const hass = provideHass(this._demoRoot);
|
||||||
|
hass.updateTranslations(null, "en");
|
||||||
|
hass.addEntities(ENTITIES);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface HTMLElementTagNameMap {
|
||||||
|
"demo-more-info-timer": DemoMoreInfoTimer;
|
||||||
|
}
|
||||||
|
}
|
3
gallery/src/pages/more-info/vacuum.markdown
Normal file
3
gallery/src/pages/more-info/vacuum.markdown
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
---
|
||||||
|
title: Vacuum
|
||||||
|
---
|
50
gallery/src/pages/more-info/vacuum.ts
Normal file
50
gallery/src/pages/more-info/vacuum.ts
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
import { html, LitElement, PropertyValues, TemplateResult } from "lit";
|
||||||
|
import { customElement, property, query } from "lit/decorators";
|
||||||
|
import "../../../../src/components/ha-card";
|
||||||
|
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";
|
||||||
|
import { VacuumEntityFeature } from "../../../../src/data/vacuum";
|
||||||
|
|
||||||
|
const ENTITIES = [
|
||||||
|
getEntity("vacuum", "first_floor_vacuum", "docked", {
|
||||||
|
friendly_name: "First floor vacuum",
|
||||||
|
supported_features:
|
||||||
|
VacuumEntityFeature.START +
|
||||||
|
VacuumEntityFeature.STOP +
|
||||||
|
VacuumEntityFeature.RETURN_HOME,
|
||||||
|
}),
|
||||||
|
];
|
||||||
|
|
||||||
|
@customElement("demo-more-info-vacuum")
|
||||||
|
class DemoMoreInfoVacuum extends LitElement {
|
||||||
|
@property() public hass!: MockHomeAssistant;
|
||||||
|
|
||||||
|
@query("demo-more-infos") private _demoRoot!: HTMLElement;
|
||||||
|
|
||||||
|
protected render(): TemplateResult {
|
||||||
|
return html`
|
||||||
|
<demo-more-infos
|
||||||
|
.hass=${this.hass}
|
||||||
|
.entities=${ENTITIES.map((ent) => ent.entityId)}
|
||||||
|
></demo-more-infos>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected firstUpdated(changedProperties: PropertyValues) {
|
||||||
|
super.firstUpdated(changedProperties);
|
||||||
|
const hass = provideHass(this._demoRoot);
|
||||||
|
hass.updateTranslations(null, "en");
|
||||||
|
hass.addEntities(ENTITIES);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface HTMLElementTagNameMap {
|
||||||
|
"demo-more-info-vacuum": DemoMoreInfoVacuum;
|
||||||
|
}
|
||||||
|
}
|
@ -1,33 +0,0 @@
|
|||||||
/**
|
|
||||||
* Update root's child element to be newElementTag replacing another existing child if any.
|
|
||||||
* Copy attributes into the child element.
|
|
||||||
*/
|
|
||||||
export default function dynamicContentUpdater(root, newElementTag, attributes) {
|
|
||||||
const rootEl = root;
|
|
||||||
let customEl;
|
|
||||||
|
|
||||||
if (rootEl.lastChild && rootEl.lastChild.tagName === newElementTag) {
|
|
||||||
customEl = rootEl.lastChild;
|
|
||||||
} else {
|
|
||||||
if (rootEl.lastChild) {
|
|
||||||
rootEl.removeChild(rootEl.lastChild);
|
|
||||||
}
|
|
||||||
// Creating an element with upper case works fine in Chrome, but in FF it doesn't immediately
|
|
||||||
// become a defined Custom Element. Polymer does that in some later pass.
|
|
||||||
customEl = document.createElement(newElementTag.toLowerCase());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (customEl.setProperties) {
|
|
||||||
customEl.setProperties(attributes);
|
|
||||||
} else {
|
|
||||||
// If custom element definition wasn't loaded yet - setProperties would be
|
|
||||||
// missing, but no harm in setting attributes one-by-one then.
|
|
||||||
Object.keys(attributes).forEach((key) => {
|
|
||||||
customEl[key] = attributes[key];
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (customEl.parentNode === null) {
|
|
||||||
rootEl.appendChild(customEl);
|
|
||||||
}
|
|
||||||
}
|
|
@ -125,7 +125,7 @@ class StateInfo extends LitElement {
|
|||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
}
|
}
|
||||||
|
|
||||||
.name[in-dialog],
|
.name[inDialog],
|
||||||
:host([secondary-line]) .name {
|
:host([secondary-line]) .name {
|
||||||
line-height: 20px;
|
line-height: 20px;
|
||||||
}
|
}
|
||||||
|
@ -1,89 +0,0 @@
|
|||||||
import "@material/mwc-button";
|
|
||||||
import { html } from "@polymer/polymer/lib/utils/html-tag";
|
|
||||||
/* eslint-plugin-disable lit */
|
|
||||||
import { PolymerElement } from "@polymer/polymer/polymer-element";
|
|
||||||
import LocalizeMixin from "../mixins/localize-mixin";
|
|
||||||
|
|
||||||
const STATES_INTERCEPTABLE = {
|
|
||||||
cleaning: {
|
|
||||||
action: "return_to_base",
|
|
||||||
service: "return_to_base",
|
|
||||||
},
|
|
||||||
docked: {
|
|
||||||
action: "start_cleaning",
|
|
||||||
service: "start",
|
|
||||||
},
|
|
||||||
idle: {
|
|
||||||
action: "start_cleaning",
|
|
||||||
service: "start",
|
|
||||||
},
|
|
||||||
off: {
|
|
||||||
action: "turn_on",
|
|
||||||
service: "turn_on",
|
|
||||||
},
|
|
||||||
on: {
|
|
||||||
action: "turn_off",
|
|
||||||
service: "turn_off",
|
|
||||||
},
|
|
||||||
paused: {
|
|
||||||
action: "resume_cleaning",
|
|
||||||
service: "start",
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
|
||||||
* @appliesMixin LocalizeMixin
|
|
||||||
*/
|
|
||||||
class HaVacuumState extends LocalizeMixin(PolymerElement) {
|
|
||||||
static get template() {
|
|
||||||
return html`
|
|
||||||
<style>
|
|
||||||
mwc-button {
|
|
||||||
top: 3px;
|
|
||||||
height: 37px;
|
|
||||||
margin-right: -0.57em;
|
|
||||||
}
|
|
||||||
mwc-button[disabled] {
|
|
||||||
background-color: transparent;
|
|
||||||
color: var(--secondary-text-color);
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<mwc-button on-click="_callService" disabled="[[!_interceptable]]"
|
|
||||||
>[[_computeLabel(stateObj.state, _interceptable)]]</mwc-button
|
|
||||||
>
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
|
|
||||||
static get properties() {
|
|
||||||
return {
|
|
||||||
hass: Object,
|
|
||||||
stateObj: Object,
|
|
||||||
_interceptable: {
|
|
||||||
type: Boolean,
|
|
||||||
computed:
|
|
||||||
"_computeInterceptable(stateObj.state, stateObj.attributes.supported_features)",
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
_computeInterceptable(state, supportedFeatures) {
|
|
||||||
return state in STATES_INTERCEPTABLE && supportedFeatures !== 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
_computeLabel(state, interceptable) {
|
|
||||||
return interceptable
|
|
||||||
? this.localize(
|
|
||||||
`ui.card.vacuum.actions.${STATES_INTERCEPTABLE[state].action}`
|
|
||||||
)
|
|
||||||
: this.localize(`component.vacuum.entity_component._.state.${state}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
_callService(ev) {
|
|
||||||
ev.stopPropagation();
|
|
||||||
const stateObj = this.stateObj;
|
|
||||||
const service = STATES_INTERCEPTABLE[stateObj.state].service;
|
|
||||||
this.hass.callService("vacuum", service, { entity_id: stateObj.entity_id });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
customElements.define("ha-vacuum-state", HaVacuumState);
|
|
111
src/components/ha-vacuum-state.ts
Normal file
111
src/components/ha-vacuum-state.ts
Normal file
@ -0,0 +1,111 @@
|
|||||||
|
import "@material/mwc-button";
|
||||||
|
import { CSSResultGroup, LitElement, TemplateResult, css, html } from "lit";
|
||||||
|
import { customElement, property } from "lit/decorators";
|
||||||
|
import { HassEntity } from "home-assistant-js-websocket";
|
||||||
|
import { haStyle } from "../resources/styles";
|
||||||
|
import { HomeAssistant } from "../types";
|
||||||
|
|
||||||
|
const STATES_INTERCEPTABLE: {
|
||||||
|
[state: string]: {
|
||||||
|
action:
|
||||||
|
| "return_to_base"
|
||||||
|
| "start_cleaning"
|
||||||
|
| "turn_on"
|
||||||
|
| "turn_off"
|
||||||
|
| "resume_cleaning";
|
||||||
|
service: string;
|
||||||
|
};
|
||||||
|
} = {
|
||||||
|
cleaning: {
|
||||||
|
action: "return_to_base",
|
||||||
|
service: "return_to_base",
|
||||||
|
},
|
||||||
|
docked: {
|
||||||
|
action: "start_cleaning",
|
||||||
|
service: "start",
|
||||||
|
},
|
||||||
|
idle: {
|
||||||
|
action: "start_cleaning",
|
||||||
|
service: "start",
|
||||||
|
},
|
||||||
|
off: {
|
||||||
|
action: "turn_on",
|
||||||
|
service: "turn_on",
|
||||||
|
},
|
||||||
|
on: {
|
||||||
|
action: "turn_off",
|
||||||
|
service: "turn_off",
|
||||||
|
},
|
||||||
|
paused: {
|
||||||
|
action: "resume_cleaning",
|
||||||
|
service: "start",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
@customElement("ha-vacuum-state")
|
||||||
|
export class HaVacuumState extends LitElement {
|
||||||
|
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||||
|
|
||||||
|
@property({ attribute: false }) public stateObj!: HassEntity;
|
||||||
|
|
||||||
|
protected render(): TemplateResult {
|
||||||
|
const interceptable = this._computeInterceptable(
|
||||||
|
this.stateObj.state,
|
||||||
|
this.stateObj.attributes.supported_features
|
||||||
|
);
|
||||||
|
return html`
|
||||||
|
<mwc-button @click=${this._callService} .disabled=${!interceptable}>
|
||||||
|
${this._computeLabel(this.stateObj.state, interceptable)}
|
||||||
|
</mwc-button>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _computeInterceptable(
|
||||||
|
state: string,
|
||||||
|
supportedFeatures: number | undefined
|
||||||
|
) {
|
||||||
|
return state in STATES_INTERCEPTABLE && supportedFeatures !== 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _computeLabel(state: string, interceptable: boolean) {
|
||||||
|
return interceptable
|
||||||
|
? this.hass.localize(
|
||||||
|
`ui.card.vacuum.actions.${STATES_INTERCEPTABLE[state].action}`
|
||||||
|
)
|
||||||
|
: this.hass.localize(
|
||||||
|
`component.vacuum.entity_component._.state.${state}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async _callService(ev) {
|
||||||
|
ev.stopPropagation();
|
||||||
|
const stateObj = this.stateObj;
|
||||||
|
const service = STATES_INTERCEPTABLE[stateObj.state].service;
|
||||||
|
await this.hass.callService("vacuum", service, {
|
||||||
|
entity_id: stateObj.entity_id,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
static get styles(): CSSResultGroup {
|
||||||
|
return [
|
||||||
|
haStyle,
|
||||||
|
css`
|
||||||
|
mwc-button {
|
||||||
|
top: 3px;
|
||||||
|
height: 37px;
|
||||||
|
margin-right: -0.57em;
|
||||||
|
}
|
||||||
|
mwc-button[disabled] {
|
||||||
|
background-color: transparent;
|
||||||
|
color: var(--secondary-text-color);
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface HTMLElementTagNameMap {
|
||||||
|
"ha-vacuum-state": HaVacuumState;
|
||||||
|
}
|
||||||
|
}
|
@ -1,135 +0,0 @@
|
|||||||
import "@polymer/iron-flex-layout/iron-flex-layout-classes";
|
|
||||||
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";
|
|
||||||
import "./ha-icon-button";
|
|
||||||
|
|
||||||
/*
|
|
||||||
* @appliesMixin EventsMixin
|
|
||||||
*/
|
|
||||||
class HaWaterHeaterControl extends EventsMixin(PolymerElement) {
|
|
||||||
static get template() {
|
|
||||||
return html`
|
|
||||||
<style include="iron-flex iron-flex-alignment"></style>
|
|
||||||
<style>
|
|
||||||
/* local DOM styles go here */
|
|
||||||
:host {
|
|
||||||
@apply --layout-flex;
|
|
||||||
@apply --layout-horizontal;
|
|
||||||
@apply --layout-justified;
|
|
||||||
}
|
|
||||||
.in-flux#target_temperature {
|
|
||||||
color: var(--error-color);
|
|
||||||
}
|
|
||||||
#target_temperature {
|
|
||||||
@apply --layout-self-center;
|
|
||||||
font-size: 200%;
|
|
||||||
direction: ltr;
|
|
||||||
}
|
|
||||||
.control-buttons {
|
|
||||||
font-size: 200%;
|
|
||||||
text-align: right;
|
|
||||||
}
|
|
||||||
ha-icon-button {
|
|
||||||
height: 48px;
|
|
||||||
width: 48px;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<!-- local DOM goes here -->
|
|
||||||
<div id="target_temperature">[[value]] [[units]]</div>
|
|
||||||
<div class="control-buttons">
|
|
||||||
<div>
|
|
||||||
<ha-icon-button on-click="incrementValue">
|
|
||||||
<ha-icon icon="hass:chevron-up"></ha-icon>
|
|
||||||
</ha-icon-button>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<ha-icon-button on-click="decrementValue">
|
|
||||||
<ha-icon icon="hass:chevron-down"></ha-icon>
|
|
||||||
</ha-icon-button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
|
|
||||||
static get properties() {
|
|
||||||
return {
|
|
||||||
value: {
|
|
||||||
type: Number,
|
|
||||||
observer: "valueChanged",
|
|
||||||
},
|
|
||||||
units: {
|
|
||||||
type: String,
|
|
||||||
},
|
|
||||||
min: {
|
|
||||||
type: Number,
|
|
||||||
},
|
|
||||||
max: {
|
|
||||||
type: Number,
|
|
||||||
},
|
|
||||||
step: {
|
|
||||||
type: Number,
|
|
||||||
value: 1,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
temperatureStateInFlux(inFlux) {
|
|
||||||
this.$.target_temperature.classList.toggle("in-flux", inFlux);
|
|
||||||
}
|
|
||||||
|
|
||||||
incrementValue() {
|
|
||||||
const newval = this.value + this.step;
|
|
||||||
if (this.value < this.max) {
|
|
||||||
this.last_changed = Date.now();
|
|
||||||
this.temperatureStateInFlux(true);
|
|
||||||
}
|
|
||||||
if (newval <= this.max) {
|
|
||||||
// If no initial target_temp
|
|
||||||
// this forces control to start
|
|
||||||
// from the min configured instead of 0
|
|
||||||
if (newval <= this.min) {
|
|
||||||
this.value = this.min;
|
|
||||||
} else {
|
|
||||||
this.value = newval;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
this.value = this.max;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
decrementValue() {
|
|
||||||
const newval = this.value - this.step;
|
|
||||||
if (this.value > this.min) {
|
|
||||||
this.last_changed = Date.now();
|
|
||||||
this.temperatureStateInFlux(true);
|
|
||||||
}
|
|
||||||
if (newval >= this.min) {
|
|
||||||
this.value = newval;
|
|
||||||
} else {
|
|
||||||
this.value = this.min;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
valueChanged() {
|
|
||||||
// when the last_changed timestamp is changed,
|
|
||||||
// trigger a potential event fire in
|
|
||||||
// the future, as long as last changed is far enough in the
|
|
||||||
// past.
|
|
||||||
if (this.last_changed) {
|
|
||||||
window.setTimeout(() => {
|
|
||||||
const now = Date.now();
|
|
||||||
if (now - this.last_changed >= 2000) {
|
|
||||||
this.fire("change");
|
|
||||||
this.temperatureStateInFlux(false);
|
|
||||||
this.last_changed = null;
|
|
||||||
}
|
|
||||||
}, 2010);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
customElements.define("ha-water_heater-control", HaWaterHeaterControl);
|
|
@ -1,62 +1,30 @@
|
|||||||
import { html } from "@polymer/polymer/lib/utils/html-tag";
|
import { customElement, property } from "lit/decorators";
|
||||||
/* eslint-plugin-disable lit */
|
import { CSSResultGroup, LitElement, TemplateResult, css, html } from "lit";
|
||||||
import { PolymerElement } from "@polymer/polymer/polymer-element";
|
import { HassEntity } from "home-assistant-js-websocket";
|
||||||
import { formatNumber } from "../common/number/format_number";
|
import { formatNumber } from "../common/number/format_number";
|
||||||
import LocalizeMixin from "../mixins/localize-mixin";
|
import { haStyle } from "../resources/styles";
|
||||||
|
import { HomeAssistant } from "../types";
|
||||||
|
|
||||||
/*
|
@customElement("ha-water_heater-state")
|
||||||
* @appliesMixin LocalizeMixin
|
export class HaWaterHeaterState extends LitElement {
|
||||||
*/
|
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||||
class HaWaterHeaterState extends LocalizeMixin(PolymerElement) {
|
|
||||||
static get template() {
|
@property({ attribute: false }) public stateObj!: HassEntity;
|
||||||
|
|
||||||
|
protected render(): TemplateResult {
|
||||||
return html`
|
return html`
|
||||||
<style>
|
|
||||||
:host {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
justify-content: center;
|
|
||||||
white-space: nowrap;
|
|
||||||
}
|
|
||||||
|
|
||||||
.target {
|
|
||||||
color: var(--primary-text-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
.current {
|
|
||||||
color: var(--secondary-text-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
.state-label {
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
|
|
||||||
.label {
|
|
||||||
direction: ltr;
|
|
||||||
display: inline-block;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<div class="target">
|
<div class="target">
|
||||||
<span class="state-label label"> [[_localizeState(stateObj)]] </span>
|
<span class="state-label label">
|
||||||
<span class="label">[[computeTarget(hass, stateObj)]]</span>
|
${this.hass.formatEntityState(this.stateObj)}
|
||||||
|
</span>
|
||||||
|
<span class="label"
|
||||||
|
>${this._computeTarget(this.hass, this.stateObj)}</span
|
||||||
|
>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<template is="dom-if" if="[[currentStatus]]">
|
|
||||||
<div class="current">
|
|
||||||
[[localize('ui.card.water_heater.currently')]]: [[currentStatus]]
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
static get properties() {
|
private _computeTarget(hass: HomeAssistant, stateObj: HassEntity) {
|
||||||
return {
|
|
||||||
hass: Object,
|
|
||||||
stateObj: Object,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
computeTarget(hass, stateObj) {
|
|
||||||
if (!hass || !stateObj) return null;
|
if (!hass || !stateObj) return null;
|
||||||
// We're using "!= null" on purpose so that we match both null and undefined.
|
// We're using "!= null" on purpose so that we match both null and undefined.
|
||||||
|
|
||||||
@ -85,5 +53,41 @@ class HaWaterHeaterState extends LocalizeMixin(PolymerElement) {
|
|||||||
_localizeState(stateObj) {
|
_localizeState(stateObj) {
|
||||||
return this.hass.formatEntityState(stateObj);
|
return this.hass.formatEntityState(stateObj);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static get styles(): CSSResultGroup {
|
||||||
|
return [
|
||||||
|
haStyle,
|
||||||
|
css`
|
||||||
|
:host {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.target {
|
||||||
|
color: var(--primary-text-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.current {
|
||||||
|
color: var(--secondary-text-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.state-label {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.label {
|
||||||
|
direction: ltr;
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface HTMLElementTagNameMap {
|
||||||
|
"ha-water_heater-state": HaWaterHeaterState;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
customElements.define("ha-water_heater-state", HaWaterHeaterState);
|
|
@ -65,7 +65,7 @@ export class MoreInfoInfo extends LitElement {
|
|||||||
? ""
|
? ""
|
||||||
: html`
|
: html`
|
||||||
<state-card-content
|
<state-card-content
|
||||||
in-dialog
|
inDialog
|
||||||
.stateObj=${stateObj}
|
.stateObj=${stateObj}
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
></state-card-content>
|
></state-card-content>
|
||||||
|
@ -1,13 +1,14 @@
|
|||||||
import { HassEntity } from "home-assistant-js-websocket";
|
import { HassEntity } from "home-assistant-js-websocket";
|
||||||
import { PropertyValues, ReactiveElement } from "lit";
|
import { LitElement, nothing } from "lit";
|
||||||
import { property } from "lit/decorators";
|
import { customElement, property } from "lit/decorators";
|
||||||
import dynamicContentUpdater from "../../common/dom/dynamic_content_updater";
|
|
||||||
import { ExtEntityRegistryEntry } from "../../data/entity_registry";
|
import { ExtEntityRegistryEntry } from "../../data/entity_registry";
|
||||||
import { importMoreInfoControl } from "../../panels/lovelace/custom-card-helpers";
|
import { importMoreInfoControl } from "../../panels/lovelace/custom-card-helpers";
|
||||||
import { HomeAssistant } from "../../types";
|
import { HomeAssistant } from "../../types";
|
||||||
import { stateMoreInfoType } from "./state_more_info_control";
|
import { stateMoreInfoType } from "./state_more_info_control";
|
||||||
|
import { dynamicElement } from "../../common/dom/dynamic-element-directive";
|
||||||
|
|
||||||
class MoreInfoContent extends ReactiveElement {
|
@customElement("more-info-content")
|
||||||
|
class MoreInfoContent extends LitElement {
|
||||||
@property({ attribute: false }) public hass?: HomeAssistant;
|
@property({ attribute: false }) public hass?: HomeAssistant;
|
||||||
|
|
||||||
@property({ attribute: false }) public stateObj?: HassEntity;
|
@property({ attribute: false }) public stateObj?: HassEntity;
|
||||||
@ -16,62 +17,31 @@ class MoreInfoContent extends ReactiveElement {
|
|||||||
|
|
||||||
@property({ attribute: false }) public editMode?: boolean;
|
@property({ attribute: false }) public editMode?: boolean;
|
||||||
|
|
||||||
private _detachedChild?: ChildNode;
|
protected render() {
|
||||||
|
|
||||||
protected createRenderRoot() {
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
// This is not a lit element, but an updating element, so we implement update
|
|
||||||
protected update(changedProps: PropertyValues): void {
|
|
||||||
super.update(changedProps);
|
|
||||||
const stateObj = this.stateObj;
|
|
||||||
const entry = this.entry;
|
|
||||||
const hass = this.hass;
|
|
||||||
const editMode = this.editMode;
|
|
||||||
|
|
||||||
if (!stateObj || !hass) {
|
|
||||||
if (this.lastChild) {
|
|
||||||
this._detachedChild = this.lastChild;
|
|
||||||
// Detach child to prevent it from doing work.
|
|
||||||
this.removeChild(this.lastChild);
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this._detachedChild) {
|
|
||||||
this.appendChild(this._detachedChild);
|
|
||||||
this._detachedChild = undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
let moreInfoType: string | undefined;
|
let moreInfoType: string | undefined;
|
||||||
|
|
||||||
if (stateObj.attributes && "custom_ui_more_info" in stateObj.attributes) {
|
if (!this.stateObj || !this.hass) return nothing;
|
||||||
moreInfoType = stateObj.attributes.custom_ui_more_info;
|
if (
|
||||||
|
this.stateObj.attributes &&
|
||||||
|
"custom_ui_more_info" in this.stateObj.attributes
|
||||||
|
) {
|
||||||
|
moreInfoType = this.stateObj.attributes.custom_ui_more_info;
|
||||||
} else {
|
} else {
|
||||||
const type = stateMoreInfoType(stateObj);
|
const type = stateMoreInfoType(this.stateObj);
|
||||||
importMoreInfoControl(type);
|
importMoreInfoControl(type);
|
||||||
moreInfoType = type === "hidden" ? undefined : `more-info-${type}`;
|
moreInfoType = type === "hidden" ? undefined : `more-info-${type}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!moreInfoType) {
|
if (!moreInfoType) return nothing;
|
||||||
if (this.lastChild) {
|
return dynamicElement(moreInfoType, {
|
||||||
this.removeChild(this.lastChild);
|
hass: this.hass,
|
||||||
}
|
stateObj: this.stateObj,
|
||||||
return;
|
entry: this.entry,
|
||||||
}
|
editMode: this.editMode,
|
||||||
|
|
||||||
dynamicContentUpdater(this, moreInfoType.toUpperCase(), {
|
|
||||||
hass,
|
|
||||||
stateObj,
|
|
||||||
entry,
|
|
||||||
editMode,
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
customElements.define("more-info-content", MoreInfoContent);
|
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
interface HTMLElementTagNameMap {
|
interface HTMLElementTagNameMap {
|
||||||
"more-info-content": MoreInfoContent;
|
"more-info-content": MoreInfoContent;
|
||||||
|
@ -8,7 +8,7 @@ import { haStyle } from "../resources/styles";
|
|||||||
import type { HomeAssistant } from "../types";
|
import type { HomeAssistant } from "../types";
|
||||||
|
|
||||||
@customElement("state-card-alert")
|
@customElement("state-card-alert")
|
||||||
export class StateCardAlert extends LitElement {
|
class StateCardAlert extends LitElement {
|
||||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||||
|
|
||||||
@property({ attribute: false }) public stateObj!: HassEntity;
|
@property({ attribute: false }) public stateObj!: HassEntity;
|
||||||
@ -78,3 +78,9 @@ export class StateCardAlert extends LitElement {
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface HTMLElementTagNameMap {
|
||||||
|
"state-card-alert": StateCardAlert;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -9,7 +9,7 @@ import { haStyle } from "../resources/styles";
|
|||||||
import { HomeAssistant } from "../types";
|
import { HomeAssistant } from "../types";
|
||||||
|
|
||||||
@customElement("state-card-button")
|
@customElement("state-card-button")
|
||||||
export class StateCardButton extends LitElement {
|
class StateCardButton extends LitElement {
|
||||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||||
|
|
||||||
@property() public stateObj!: HassEntity;
|
@property() public stateObj!: HassEntity;
|
||||||
|
@ -1,55 +0,0 @@
|
|||||||
import "@polymer/iron-flex-layout/iron-flex-layout-classes";
|
|
||||||
import { html } from "@polymer/polymer/lib/utils/html-tag";
|
|
||||||
/* eslint-plugin-disable lit */
|
|
||||||
import { PolymerElement } from "@polymer/polymer/polymer-element";
|
|
||||||
import "../components/entity/state-info";
|
|
||||||
import "../components/ha-climate-state";
|
|
||||||
|
|
||||||
class StateCardClimate extends PolymerElement {
|
|
||||||
static get template() {
|
|
||||||
return html`
|
|
||||||
<style include="iron-flex iron-flex-alignment"></style>
|
|
||||||
<style>
|
|
||||||
:host {
|
|
||||||
@apply --paper-font-body1;
|
|
||||||
line-height: 1.5;
|
|
||||||
}
|
|
||||||
|
|
||||||
ha-climate-state {
|
|
||||||
margin-left: 16px;
|
|
||||||
text-align: right;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<div class="horizontal justified layout">
|
|
||||||
${this.stateInfoTemplate}
|
|
||||||
<ha-climate-state
|
|
||||||
hass="[[hass]]"
|
|
||||||
state-obj="[[stateObj]]"
|
|
||||||
></ha-climate-state>
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
|
|
||||||
static get stateInfoTemplate() {
|
|
||||||
return html`
|
|
||||||
<state-info
|
|
||||||
hass="[[hass]]"
|
|
||||||
state-obj="[[stateObj]]"
|
|
||||||
in-dialog="[[inDialog]]"
|
|
||||||
></state-info>
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
|
|
||||||
static get properties() {
|
|
||||||
return {
|
|
||||||
hass: Object,
|
|
||||||
stateObj: Object,
|
|
||||||
inDialog: {
|
|
||||||
type: Boolean,
|
|
||||||
value: false,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
customElements.define("state-card-climate", StateCardClimate);
|
|
54
src/state-summary/state-card-climate.ts
Normal file
54
src/state-summary/state-card-climate.ts
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
import type { HassEntity } from "home-assistant-js-websocket";
|
||||||
|
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
||||||
|
import "../components/entity/state-info";
|
||||||
|
import "../components/ha-climate-state";
|
||||||
|
import { customElement, property } from "lit/decorators";
|
||||||
|
import { HomeAssistant } from "../types";
|
||||||
|
import { haStyle } from "../resources/styles";
|
||||||
|
|
||||||
|
@customElement("state-card-climate")
|
||||||
|
class StateCardClimate extends LitElement {
|
||||||
|
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||||
|
|
||||||
|
@property({ attribute: false }) public stateObj!: HassEntity;
|
||||||
|
|
||||||
|
@property({ type: Boolean }) public inDialog = false;
|
||||||
|
|
||||||
|
protected render(): TemplateResult {
|
||||||
|
return html`
|
||||||
|
<div class="horizontal justified layout">
|
||||||
|
<state-info
|
||||||
|
.hass=${this.hass}
|
||||||
|
.stateObj=${this.stateObj}
|
||||||
|
.inDialog=${this.inDialog}
|
||||||
|
></state-info>
|
||||||
|
<ha-climate-state
|
||||||
|
.hass=${this.hass}
|
||||||
|
.stateObj=${this.stateObj}
|
||||||
|
></ha-climate-state>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
static get styles(): CSSResultGroup {
|
||||||
|
return [
|
||||||
|
haStyle,
|
||||||
|
css`
|
||||||
|
:host {
|
||||||
|
line-height: 1.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
ha-climate-state {
|
||||||
|
margin-left: 16px;
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface HTMLElementTagNameMap {
|
||||||
|
"state-card-climate": StateCardClimate;
|
||||||
|
}
|
||||||
|
}
|
@ -1,69 +0,0 @@
|
|||||||
import "@material/mwc-button";
|
|
||||||
import "@polymer/iron-flex-layout/iron-flex-layout-classes";
|
|
||||||
import { html } from "@polymer/polymer/lib/utils/html-tag";
|
|
||||||
/* eslint-plugin-disable lit */
|
|
||||||
import { PolymerElement } from "@polymer/polymer/polymer-element";
|
|
||||||
import { computeStateDisplay } from "../common/entity/compute_state_display";
|
|
||||||
import "../components/entity/state-info";
|
|
||||||
import LocalizeMixin from "../mixins/localize-mixin";
|
|
||||||
|
|
||||||
/*
|
|
||||||
* @appliesMixin LocalizeMixin
|
|
||||||
*/
|
|
||||||
class StateCardConfigurator extends LocalizeMixin(PolymerElement) {
|
|
||||||
static get template() {
|
|
||||||
return html`
|
|
||||||
<style include="iron-flex iron-flex-alignment"></style>
|
|
||||||
<style>
|
|
||||||
mwc-button {
|
|
||||||
top: 3px;
|
|
||||||
height: 37px;
|
|
||||||
margin-right: -0.57em;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<div class="horizontal justified layout">
|
|
||||||
${this.stateInfoTemplate}
|
|
||||||
<mwc-button hidden$="[[inDialog]]"
|
|
||||||
>[[_localizeState(stateObj)]]</mwc-button
|
|
||||||
>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- pre load the image so the dialog is rendered the proper size -->
|
|
||||||
<template is="dom-if" if="[[stateObj.attributes.description_image]]">
|
|
||||||
<img hidden="" alt="" src="[[stateObj.attributes.description_image]]" />
|
|
||||||
</template>
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
|
|
||||||
static get stateInfoTemplate() {
|
|
||||||
return html`
|
|
||||||
<state-info
|
|
||||||
hass="[[hass]]"
|
|
||||||
state-obj="[[stateObj]]"
|
|
||||||
in-dialog="[[inDialog]]"
|
|
||||||
></state-info>
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
|
|
||||||
static get properties() {
|
|
||||||
return {
|
|
||||||
hass: Object,
|
|
||||||
stateObj: Object,
|
|
||||||
inDialog: {
|
|
||||||
type: Boolean,
|
|
||||||
value: false,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
_localizeState(stateObj) {
|
|
||||||
return computeStateDisplay(
|
|
||||||
this.hass.localize,
|
|
||||||
stateObj,
|
|
||||||
this.hass.locale,
|
|
||||||
this.hass.entities
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
customElements.define("state-card-configurator", StateCardConfigurator);
|
|
59
src/state-summary/state-card-configurator.ts
Normal file
59
src/state-summary/state-card-configurator.ts
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
import "@material/mwc-button";
|
||||||
|
import { HassEntity } from "home-assistant-js-websocket";
|
||||||
|
import "../components/entity/state-info";
|
||||||
|
import { customElement, property } from "lit/decorators";
|
||||||
|
import {
|
||||||
|
CSSResultGroup,
|
||||||
|
LitElement,
|
||||||
|
TemplateResult,
|
||||||
|
css,
|
||||||
|
html,
|
||||||
|
nothing,
|
||||||
|
} from "lit";
|
||||||
|
import { HomeAssistant } from "../types";
|
||||||
|
import { haStyle } from "../resources/styles";
|
||||||
|
|
||||||
|
@customElement("state-card-configurator")
|
||||||
|
class StateCardConfigurator extends LitElement {
|
||||||
|
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||||
|
|
||||||
|
@property({ attribute: false }) public stateObj!: HassEntity;
|
||||||
|
|
||||||
|
@property({ type: Boolean }) public inDialog = false;
|
||||||
|
|
||||||
|
protected render(): TemplateResult {
|
||||||
|
return html`
|
||||||
|
<div class="horizontal justified layout">
|
||||||
|
<state-info
|
||||||
|
.hass=${this.hass}
|
||||||
|
.stateObj=${this.stateObj}
|
||||||
|
.inDialog=${this.inDialog}
|
||||||
|
></state-info>
|
||||||
|
${this.inDialog
|
||||||
|
? html`<mwc-button
|
||||||
|
>${this.hass.formatEntityState(this.stateObj)}</mwc-button
|
||||||
|
>`
|
||||||
|
: nothing}
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
static get styles(): CSSResultGroup {
|
||||||
|
return [
|
||||||
|
haStyle,
|
||||||
|
css`
|
||||||
|
mwc-button {
|
||||||
|
top: 3px;
|
||||||
|
height: 37px;
|
||||||
|
margin-right: -0.57em;
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface HTMLElementTagNameMap {
|
||||||
|
"state-card-configurator": StateCardConfigurator;
|
||||||
|
}
|
||||||
|
}
|
@ -1,62 +0,0 @@
|
|||||||
/* eslint-plugin-disable lit */
|
|
||||||
import { PolymerElement } from "@polymer/polymer/polymer-element";
|
|
||||||
import dynamicContentUpdater from "../common/dom/dynamic_content_updater";
|
|
||||||
import { stateCardType } from "../common/entity/state_card_type";
|
|
||||||
import "./state-card-alert";
|
|
||||||
import "./state-card-button";
|
|
||||||
import "./state-card-climate";
|
|
||||||
import "./state-card-configurator";
|
|
||||||
import "./state-card-cover";
|
|
||||||
import "./state-card-display";
|
|
||||||
import "./state-card-event";
|
|
||||||
import "./state-card-humidifier";
|
|
||||||
import "./state-card-input_button";
|
|
||||||
import "./state-card-input_number";
|
|
||||||
import "./state-card-input_select";
|
|
||||||
import "./state-card-input_text";
|
|
||||||
import "./state-card-lawn_mower";
|
|
||||||
import "./state-card-lock";
|
|
||||||
import "./state-card-media_player";
|
|
||||||
import "./state-card-number";
|
|
||||||
import "./state-card-scene";
|
|
||||||
import "./state-card-script";
|
|
||||||
import "./state-card-select";
|
|
||||||
import "./state-card-text";
|
|
||||||
import "./state-card-timer";
|
|
||||||
import "./state-card-toggle";
|
|
||||||
import "./state-card-update";
|
|
||||||
import "./state-card-vacuum";
|
|
||||||
import "./state-card-water_heater";
|
|
||||||
|
|
||||||
class StateCardContent extends PolymerElement {
|
|
||||||
static get properties() {
|
|
||||||
return {
|
|
||||||
hass: Object,
|
|
||||||
stateObj: Object,
|
|
||||||
inDialog: {
|
|
||||||
type: Boolean,
|
|
||||||
value: false,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
static get observers() {
|
|
||||||
return ["inputChanged(hass, inDialog, stateObj)"];
|
|
||||||
}
|
|
||||||
|
|
||||||
inputChanged(hass, inDialog, stateObj) {
|
|
||||||
let stateCard;
|
|
||||||
if (!stateObj || !hass) return;
|
|
||||||
if (stateObj.attributes && "custom_ui_state_card" in stateObj.attributes) {
|
|
||||||
stateCard = stateObj.attributes.custom_ui_state_card;
|
|
||||||
} else {
|
|
||||||
stateCard = "state-card-" + stateCardType(hass, stateObj);
|
|
||||||
}
|
|
||||||
dynamicContentUpdater(this, stateCard.toUpperCase(), {
|
|
||||||
hass: hass,
|
|
||||||
stateObj: stateObj,
|
|
||||||
inDialog: inDialog,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
customElements.define("state-card-content", StateCardContent);
|
|
65
src/state-summary/state-card-content.ts
Normal file
65
src/state-summary/state-card-content.ts
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
import { LitElement, nothing } from "lit";
|
||||||
|
import { HassEntity } from "home-assistant-js-websocket";
|
||||||
|
import { customElement, property } from "lit/decorators";
|
||||||
|
import { HomeAssistant } from "../types";
|
||||||
|
import { dynamicElement } from "../common/dom/dynamic-element-directive";
|
||||||
|
import { stateCardType } from "../common/entity/state_card_type";
|
||||||
|
import "./state-card-alert";
|
||||||
|
import "./state-card-button";
|
||||||
|
import "./state-card-climate";
|
||||||
|
import "./state-card-configurator";
|
||||||
|
import "./state-card-cover";
|
||||||
|
import "./state-card-display";
|
||||||
|
import "./state-card-event";
|
||||||
|
import "./state-card-humidifier";
|
||||||
|
import "./state-card-input_button";
|
||||||
|
import "./state-card-input_number";
|
||||||
|
import "./state-card-input_select";
|
||||||
|
import "./state-card-input_text";
|
||||||
|
import "./state-card-lawn_mower";
|
||||||
|
import "./state-card-lock";
|
||||||
|
import "./state-card-media_player";
|
||||||
|
import "./state-card-number";
|
||||||
|
import "./state-card-scene";
|
||||||
|
import "./state-card-script";
|
||||||
|
import "./state-card-select";
|
||||||
|
import "./state-card-text";
|
||||||
|
import "./state-card-timer";
|
||||||
|
import "./state-card-toggle";
|
||||||
|
import "./state-card-update";
|
||||||
|
import "./state-card-vacuum";
|
||||||
|
import "./state-card-water_heater";
|
||||||
|
|
||||||
|
@customElement("state-card-content")
|
||||||
|
class StateCardContent extends LitElement {
|
||||||
|
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||||
|
|
||||||
|
@property({ attribute: false }) public stateObj!: HassEntity;
|
||||||
|
|
||||||
|
@property({ type: Boolean }) public inDialog = false;
|
||||||
|
|
||||||
|
protected render() {
|
||||||
|
let stateCard: string;
|
||||||
|
if (!this.stateObj || !this.hass) return nothing;
|
||||||
|
if (
|
||||||
|
this.stateObj.attributes &&
|
||||||
|
"custom_ui_state_card" in this.stateObj.attributes
|
||||||
|
) {
|
||||||
|
stateCard = this.stateObj.attributes.custom_ui_state_card;
|
||||||
|
} else {
|
||||||
|
stateCard = "state-card-" + stateCardType(this.hass, this.stateObj);
|
||||||
|
}
|
||||||
|
|
||||||
|
return dynamicElement(stateCard, {
|
||||||
|
hass: this.hass,
|
||||||
|
stateObj: this.stateObj,
|
||||||
|
inDialog: this.inDialog,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface HTMLElementTagNameMap {
|
||||||
|
"state-card-content": StateCardContent;
|
||||||
|
}
|
||||||
|
}
|
@ -12,7 +12,7 @@ import { haStyle } from "../resources/styles";
|
|||||||
import type { HomeAssistant } from "../types";
|
import type { HomeAssistant } from "../types";
|
||||||
|
|
||||||
@customElement("state-card-display")
|
@customElement("state-card-display")
|
||||||
export class StateCardDisplay extends LitElement {
|
class StateCardDisplay extends LitElement {
|
||||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||||
|
|
||||||
@property({ attribute: false }) public stateObj!: HassEntity;
|
@property({ attribute: false }) public stateObj!: HassEntity;
|
||||||
@ -93,3 +93,9 @@ export class StateCardDisplay extends LitElement {
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface HTMLElementTagNameMap {
|
||||||
|
"state-card-display": StateCardDisplay;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -7,7 +7,7 @@ import { haStyle } from "../resources/styles";
|
|||||||
import { HomeAssistant } from "../types";
|
import { HomeAssistant } from "../types";
|
||||||
|
|
||||||
@customElement("state-card-event")
|
@customElement("state-card-event")
|
||||||
export class StateCardEvent extends LitElement {
|
class StateCardEvent extends LitElement {
|
||||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||||
|
|
||||||
@property() public stateObj!: HassEntity;
|
@property() public stateObj!: HassEntity;
|
||||||
|
@ -1,55 +0,0 @@
|
|||||||
import "@polymer/iron-flex-layout/iron-flex-layout-classes";
|
|
||||||
import { html } from "@polymer/polymer/lib/utils/html-tag";
|
|
||||||
/* eslint-plugin-disable lit */
|
|
||||||
import { PolymerElement } from "@polymer/polymer/polymer-element";
|
|
||||||
import "../components/entity/state-info";
|
|
||||||
import "../components/ha-humidifier-state";
|
|
||||||
|
|
||||||
class StateCardHumidifier extends PolymerElement {
|
|
||||||
static get template() {
|
|
||||||
return html`
|
|
||||||
<style include="iron-flex iron-flex-alignment"></style>
|
|
||||||
<style>
|
|
||||||
:host {
|
|
||||||
@apply --paper-font-body1;
|
|
||||||
line-height: 1.5;
|
|
||||||
}
|
|
||||||
|
|
||||||
ha-humidifier-state {
|
|
||||||
margin-left: 16px;
|
|
||||||
text-align: right;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<div class="horizontal justified layout">
|
|
||||||
${this.stateInfoTemplate}
|
|
||||||
<ha-humidifier-state
|
|
||||||
hass="[[hass]]"
|
|
||||||
state-obj="[[stateObj]]"
|
|
||||||
></ha-humidifier-state>
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
|
|
||||||
static get stateInfoTemplate() {
|
|
||||||
return html`
|
|
||||||
<state-info
|
|
||||||
hass="[[hass]]"
|
|
||||||
state-obj="[[stateObj]]"
|
|
||||||
in-dialog="[[inDialog]]"
|
|
||||||
></state-info>
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
|
|
||||||
static get properties() {
|
|
||||||
return {
|
|
||||||
hass: Object,
|
|
||||||
stateObj: Object,
|
|
||||||
inDialog: {
|
|
||||||
type: Boolean,
|
|
||||||
value: false,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
customElements.define("state-card-humidifier", StateCardHumidifier);
|
|
56
src/state-summary/state-card-humidifier.ts
Normal file
56
src/state-summary/state-card-humidifier.ts
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
import type { HassEntity } from "home-assistant-js-websocket";
|
||||||
|
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
||||||
|
import { customElement, property } from "lit/decorators";
|
||||||
|
|
||||||
|
import "../components/entity/state-info";
|
||||||
|
import "../components/ha-humidifier-state";
|
||||||
|
import { HomeAssistant } from "../types";
|
||||||
|
import { haStyle } from "../resources/styles";
|
||||||
|
|
||||||
|
@customElement("state-card-humidifier")
|
||||||
|
class StateCardHumidifier extends LitElement {
|
||||||
|
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||||
|
|
||||||
|
@property({ attribute: false }) public stateObj!: HassEntity;
|
||||||
|
|
||||||
|
@property({ type: Boolean }) public inDialog = false;
|
||||||
|
|
||||||
|
protected render(): TemplateResult {
|
||||||
|
return html`
|
||||||
|
<div class="horizontal justified layout">
|
||||||
|
<state-info
|
||||||
|
.hass=${this.hass}
|
||||||
|
.stateObj=${this.stateObj}
|
||||||
|
.inDialog=${this.inDialog}
|
||||||
|
>
|
||||||
|
</state-info>
|
||||||
|
<ha-humidifier-state
|
||||||
|
.hass=${this.hass}
|
||||||
|
.stateObj=${this.stateObj}
|
||||||
|
></ha-humidifier-state>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
static get styles(): CSSResultGroup {
|
||||||
|
return [
|
||||||
|
haStyle,
|
||||||
|
css`
|
||||||
|
:host {
|
||||||
|
line-height: 1.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
ha-humidifier-state {
|
||||||
|
margin-left: 16px;
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface HTMLElementTagNameMap {
|
||||||
|
"state-card-humidifier": StateCardHumidifier;
|
||||||
|
}
|
||||||
|
}
|
@ -9,7 +9,7 @@ import { haStyle } from "../resources/styles";
|
|||||||
import { HomeAssistant } from "../types";
|
import { HomeAssistant } from "../types";
|
||||||
|
|
||||||
@customElement("state-card-input_button")
|
@customElement("state-card-input_button")
|
||||||
export class StateCardInputButton extends LitElement {
|
class StateCardInputButton extends LitElement {
|
||||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||||
|
|
||||||
@property() public stateObj!: HassEntity;
|
@property() public stateObj!: HassEntity;
|
||||||
|
@ -1,90 +0,0 @@
|
|||||||
/* eslint-plugin-disable lit */
|
|
||||||
import "@polymer/iron-flex-layout/iron-flex-layout-classes";
|
|
||||||
import { html } from "@polymer/polymer/lib/utils/html-tag";
|
|
||||||
/* eslint-plugin-disable lit */
|
|
||||||
import { PolymerElement } from "@polymer/polymer/polymer-element";
|
|
||||||
import "../components/entity/state-info";
|
|
||||||
import "../components/ha-textfield";
|
|
||||||
|
|
||||||
class StateCardInputText extends PolymerElement {
|
|
||||||
static get template() {
|
|
||||||
return html`
|
|
||||||
<style include="iron-flex iron-flex-alignment"></style>
|
|
||||||
<style>
|
|
||||||
ha-textfield {
|
|
||||||
margin-left: 16px;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<div class="horizontal justified layout">
|
|
||||||
${this.stateInfoTemplate}
|
|
||||||
<ha-textfield
|
|
||||||
minlength="[[stateObj.attributes.min]]"
|
|
||||||
maxlength="[[stateObj.attributes.max]]"
|
|
||||||
value="[[value]]"
|
|
||||||
auto-validate="[[stateObj.attributes.pattern]]"
|
|
||||||
pattern="[[stateObj.attributes.pattern]]"
|
|
||||||
type="[[stateObj.attributes.mode]]"
|
|
||||||
on-input="onInput"
|
|
||||||
on-change="selectedValueChanged"
|
|
||||||
on-click="stopPropagation"
|
|
||||||
placeholder="(empty value)"
|
|
||||||
>
|
|
||||||
</ha-textfield>
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
|
|
||||||
static get stateInfoTemplate() {
|
|
||||||
return html`
|
|
||||||
<state-info
|
|
||||||
hass="[[hass]]"
|
|
||||||
state-obj="[[stateObj]]"
|
|
||||||
in-dialog="[[inDialog]]"
|
|
||||||
></state-info>
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
|
|
||||||
static get properties() {
|
|
||||||
return {
|
|
||||||
hass: Object,
|
|
||||||
|
|
||||||
inDialog: {
|
|
||||||
type: Boolean,
|
|
||||||
value: false,
|
|
||||||
},
|
|
||||||
|
|
||||||
stateObj: {
|
|
||||||
type: Object,
|
|
||||||
observer: "stateObjectChanged",
|
|
||||||
},
|
|
||||||
|
|
||||||
pattern: String,
|
|
||||||
value: String,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
stateObjectChanged(newVal) {
|
|
||||||
this.value = newVal.state;
|
|
||||||
}
|
|
||||||
|
|
||||||
onInput(ev) {
|
|
||||||
this.value = ev.target.value;
|
|
||||||
}
|
|
||||||
|
|
||||||
selectedValueChanged() {
|
|
||||||
if (this.value === this.stateObj.state) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.hass.callService("input_text", "set_value", {
|
|
||||||
value: this.value,
|
|
||||||
entity_id: this.stateObj.entity_id,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
stopPropagation(ev) {
|
|
||||||
ev.stopPropagation();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
customElements.define("state-card-input_text", StateCardInputText);
|
|
89
src/state-summary/state-card-input_text.ts
Normal file
89
src/state-summary/state-card-input_text.ts
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
import type { HassEntity } from "home-assistant-js-websocket";
|
||||||
|
import {
|
||||||
|
css,
|
||||||
|
CSSResultGroup,
|
||||||
|
html,
|
||||||
|
LitElement,
|
||||||
|
PropertyValues,
|
||||||
|
TemplateResult,
|
||||||
|
} from "lit";
|
||||||
|
import { customElement, property, state } from "lit/decorators";
|
||||||
|
import "../components/entity/state-info";
|
||||||
|
import "../components/ha-textfield";
|
||||||
|
import { HomeAssistant } from "../types";
|
||||||
|
import { haStyle } from "../resources/styles";
|
||||||
|
import { stopPropagation } from "../common/dom/stop_propagation";
|
||||||
|
|
||||||
|
@customElement("state-card-input_text")
|
||||||
|
class StateCardInputText extends LitElement {
|
||||||
|
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||||
|
|
||||||
|
@property({ attribute: false }) public stateObj!: HassEntity;
|
||||||
|
|
||||||
|
@property({ type: Boolean }) public inDialog = false;
|
||||||
|
|
||||||
|
@state() public value: string = "";
|
||||||
|
|
||||||
|
protected render(): TemplateResult {
|
||||||
|
return html`
|
||||||
|
<div class="horizontal justified layout">
|
||||||
|
<state-info
|
||||||
|
.hass=${this.hass}
|
||||||
|
.stateObj=${this.stateObj}
|
||||||
|
.inDialog=${this.inDialog}
|
||||||
|
></state-info
|
||||||
|
><ha-textfield
|
||||||
|
.minlength=${this.stateObj.attributes.min}
|
||||||
|
.maxlength=${this.stateObj.attributes.max}
|
||||||
|
.value=${this.value}
|
||||||
|
.auto-validate=${this.stateObj.attributes.pattern}
|
||||||
|
.pattern=${this.stateObj.attributes.pattern}
|
||||||
|
.type=${this.stateObj.attributes.mode}
|
||||||
|
@input=${this._onInput}
|
||||||
|
@change=${this._selectedValueChanged}
|
||||||
|
@click=${stopPropagation}
|
||||||
|
placeholder="(empty value)"
|
||||||
|
>
|
||||||
|
</ha-textfield>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected willUpdate(changedProp: PropertyValues): void {
|
||||||
|
super.willUpdate(changedProp);
|
||||||
|
if (changedProp.has("stateObj")) {
|
||||||
|
this.value = this.stateObj.state;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private _onInput(ev) {
|
||||||
|
this.value = ev.target.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async _selectedValueChanged() {
|
||||||
|
if (this.value === this.stateObj.state) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
await this.hass.callService("input_text", "set_value", {
|
||||||
|
value: this.value,
|
||||||
|
entity_id: this.stateObj.entity_id,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
static get styles(): CSSResultGroup {
|
||||||
|
return [
|
||||||
|
haStyle,
|
||||||
|
css`
|
||||||
|
ha-textfield {
|
||||||
|
margin-left: 16px;
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface HTMLElementTagNameMap {
|
||||||
|
"state-card-input_text": StateCardInputText;
|
||||||
|
}
|
||||||
|
}
|
@ -1,95 +0,0 @@
|
|||||||
import "@material/mwc-button";
|
|
||||||
import "@polymer/iron-flex-layout/iron-flex-layout-classes";
|
|
||||||
import { html } from "@polymer/polymer/lib/utils/html-tag";
|
|
||||||
/* eslint-plugin-disable lit */
|
|
||||||
import { PolymerElement } from "@polymer/polymer/polymer-element";
|
|
||||||
import { supportsFeature } from "../common/entity/supports-feature";
|
|
||||||
import "../components/entity/state-info";
|
|
||||||
import LocalizeMixin from "../mixins/localize-mixin";
|
|
||||||
import { LockEntityFeature } from "../data/lock";
|
|
||||||
|
|
||||||
/*
|
|
||||||
* @appliesMixin LocalizeMixin
|
|
||||||
*/
|
|
||||||
class StateCardLock extends LocalizeMixin(PolymerElement) {
|
|
||||||
static get template() {
|
|
||||||
return html`
|
|
||||||
<style include="iron-flex iron-flex-alignment"></style>
|
|
||||||
<style>
|
|
||||||
mwc-button {
|
|
||||||
top: 3px;
|
|
||||||
height: 37px;
|
|
||||||
margin-right: -0.57em;
|
|
||||||
}
|
|
||||||
[hidden] {
|
|
||||||
display: none !important;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<div class="horizontal justified layout">
|
|
||||||
${this.stateInfoTemplate}
|
|
||||||
<mwc-button
|
|
||||||
on-click="_callService"
|
|
||||||
data-service="open"
|
|
||||||
hidden$="[[!supportsOpen]]"
|
|
||||||
>[[localize('ui.card.lock.open')]]</mwc-button
|
|
||||||
>
|
|
||||||
<mwc-button
|
|
||||||
on-click="_callService"
|
|
||||||
data-service="unlock"
|
|
||||||
hidden$="[[!isLocked]]"
|
|
||||||
>[[localize('ui.card.lock.unlock')]]</mwc-button
|
|
||||||
>
|
|
||||||
<mwc-button
|
|
||||||
on-click="_callService"
|
|
||||||
data-service="lock"
|
|
||||||
hidden$="[[isLocked]]"
|
|
||||||
>[[localize('ui.card.lock.lock')]]</mwc-button
|
|
||||||
>
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
|
|
||||||
static get stateInfoTemplate() {
|
|
||||||
return html`
|
|
||||||
<state-info
|
|
||||||
hass="[[hass]]"
|
|
||||||
state-obj="[[stateObj]]"
|
|
||||||
in-dialog="[[inDialog]]"
|
|
||||||
></state-info>
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
|
|
||||||
static get properties() {
|
|
||||||
return {
|
|
||||||
hass: Object,
|
|
||||||
stateObj: {
|
|
||||||
type: Object,
|
|
||||||
observer: "_stateObjChanged",
|
|
||||||
},
|
|
||||||
inDialog: {
|
|
||||||
type: Boolean,
|
|
||||||
value: false,
|
|
||||||
},
|
|
||||||
isLocked: Boolean,
|
|
||||||
supportsOpen: Boolean,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
_stateObjChanged(newVal) {
|
|
||||||
if (newVal) {
|
|
||||||
this.isLocked = newVal.state === "locked";
|
|
||||||
this.supportsOpen = supportsFeature(newVal, LockEntityFeature.OPEN);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_callService(ev) {
|
|
||||||
ev.stopPropagation();
|
|
||||||
const service = ev.target.dataset.service;
|
|
||||||
const data = {
|
|
||||||
entity_id: this.stateObj.entity_id,
|
|
||||||
};
|
|
||||||
this.hass.callService("lock", service, data);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
customElements.define("state-card-lock", StateCardLock);
|
|
83
src/state-summary/state-card-lock.ts
Normal file
83
src/state-summary/state-card-lock.ts
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
import "@material/mwc-button";
|
||||||
|
import type { HassEntity } from "home-assistant-js-websocket";
|
||||||
|
import {
|
||||||
|
css,
|
||||||
|
CSSResultGroup,
|
||||||
|
html,
|
||||||
|
LitElement,
|
||||||
|
nothing,
|
||||||
|
TemplateResult,
|
||||||
|
} from "lit";
|
||||||
|
import { customElement, property } from "lit/decorators";
|
||||||
|
import { supportsFeature } from "../common/entity/supports-feature";
|
||||||
|
import "../components/entity/state-info";
|
||||||
|
import { LockEntityFeature } from "../data/lock";
|
||||||
|
import { HomeAssistant } from "../types";
|
||||||
|
import { haStyle } from "../resources/styles";
|
||||||
|
|
||||||
|
@customElement("state-card-lock")
|
||||||
|
class StateCardLock extends LitElement {
|
||||||
|
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||||
|
|
||||||
|
@property({ attribute: false }) public stateObj!: HassEntity;
|
||||||
|
|
||||||
|
@property({ type: Boolean }) public inDialog = false;
|
||||||
|
|
||||||
|
protected render(): TemplateResult {
|
||||||
|
const isLocked = this.stateObj.state === "locked";
|
||||||
|
const supportsOpen = supportsFeature(this.stateObj, LockEntityFeature.OPEN);
|
||||||
|
|
||||||
|
return html`
|
||||||
|
<div class="horizontal justified layout">
|
||||||
|
<state-info
|
||||||
|
.hass=${this.hass}
|
||||||
|
.stateObj=${this.stateObj}
|
||||||
|
.inDialog=${this.inDialog}
|
||||||
|
></state-info>
|
||||||
|
${!supportsOpen
|
||||||
|
? html`<mwc-button @click=${this._callService} data-service="open"
|
||||||
|
>${this.hass.localize("ui.card.lock.open")}</mwc-button
|
||||||
|
>`
|
||||||
|
: nothing}
|
||||||
|
${isLocked
|
||||||
|
? html` <mwc-button @click=${this._callService} data-service="unlock"
|
||||||
|
>${this.hass.localize("ui.card.lock.unlock")}</mwc-button
|
||||||
|
>`
|
||||||
|
: nothing}
|
||||||
|
${!isLocked
|
||||||
|
? html`<mwc-button @click=${this._callService} data-service="lock"
|
||||||
|
>${this.hass.localize("ui.card.lock.lock")}</mwc-button
|
||||||
|
>`
|
||||||
|
: nothing}
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async _callService(ev) {
|
||||||
|
ev.stopPropagation();
|
||||||
|
const service = ev.target.dataset.service;
|
||||||
|
const data = {
|
||||||
|
entity_id: this.stateObj.entity_id,
|
||||||
|
};
|
||||||
|
await this.hass.callService("lock", service, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
static get styles(): CSSResultGroup {
|
||||||
|
return [
|
||||||
|
haStyle,
|
||||||
|
css`
|
||||||
|
mwc-button {
|
||||||
|
top: 3px;
|
||||||
|
height: 37px;
|
||||||
|
margin-right: -0.57em;
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface HTMLElementTagNameMap {
|
||||||
|
"state-card-lock": StateCardLock;
|
||||||
|
}
|
||||||
|
}
|
@ -1,97 +0,0 @@
|
|||||||
import "@polymer/iron-flex-layout/iron-flex-layout-classes";
|
|
||||||
import { html } from "@polymer/polymer/lib/utils/html-tag";
|
|
||||||
/* eslint-plugin-disable lit */
|
|
||||||
import { PolymerElement } from "@polymer/polymer/polymer-element";
|
|
||||||
import { computeStateDisplay } from "../common/entity/compute_state_display";
|
|
||||||
import "../components/entity/state-info";
|
|
||||||
import LocalizeMixin from "../mixins/localize-mixin";
|
|
||||||
import HassMediaPlayerEntity from "../util/hass-media-player-model";
|
|
||||||
|
|
||||||
/*
|
|
||||||
* @appliesMixin LocalizeMixin
|
|
||||||
*/
|
|
||||||
class StateCardMediaPlayer extends LocalizeMixin(PolymerElement) {
|
|
||||||
static get template() {
|
|
||||||
return html`
|
|
||||||
<style include="iron-flex iron-flex-alignment"></style>
|
|
||||||
<style>
|
|
||||||
:host {
|
|
||||||
line-height: 1.5;
|
|
||||||
}
|
|
||||||
|
|
||||||
.state {
|
|
||||||
@apply --paper-font-common-nowrap;
|
|
||||||
@apply --paper-font-body1;
|
|
||||||
margin-left: 16px;
|
|
||||||
text-align: right;
|
|
||||||
}
|
|
||||||
|
|
||||||
.main-text {
|
|
||||||
@apply --paper-font-common-nowrap;
|
|
||||||
color: var(--primary-text-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
.main-text[take-height] {
|
|
||||||
line-height: 40px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.secondary-text {
|
|
||||||
@apply --paper-font-common-nowrap;
|
|
||||||
color: var(--secondary-text-color);
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<div class="horizontal justified layout">
|
|
||||||
${this.stateInfoTemplate}
|
|
||||||
<div class="state">
|
|
||||||
<div class="main-text" take-height$="[[!playerObj.secondaryTitle]]">
|
|
||||||
[[computePrimaryText(localize, playerObj)]]
|
|
||||||
</div>
|
|
||||||
<div class="secondary-text">[[playerObj.secondaryTitle]]</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
|
|
||||||
static get stateInfoTemplate() {
|
|
||||||
return html`
|
|
||||||
<state-info
|
|
||||||
hass="[[hass]]"
|
|
||||||
state-obj="[[stateObj]]"
|
|
||||||
in-dialog="[[inDialog]]"
|
|
||||||
></state-info>
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
|
|
||||||
static get properties() {
|
|
||||||
return {
|
|
||||||
hass: Object,
|
|
||||||
stateObj: Object,
|
|
||||||
inDialog: {
|
|
||||||
type: Boolean,
|
|
||||||
value: false,
|
|
||||||
},
|
|
||||||
playerObj: {
|
|
||||||
type: Object,
|
|
||||||
computed: "computePlayerObj(hass, stateObj)",
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
computePlayerObj(hass, stateObj) {
|
|
||||||
return new HassMediaPlayerEntity(hass, stateObj);
|
|
||||||
}
|
|
||||||
|
|
||||||
computePrimaryText(localize, playerObj) {
|
|
||||||
return (
|
|
||||||
playerObj.primaryTitle ||
|
|
||||||
computeStateDisplay(
|
|
||||||
localize,
|
|
||||||
playerObj.stateObj,
|
|
||||||
this.hass.locale,
|
|
||||||
this.hass.entities
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
customElements.define("state-card-media_player", StateCardMediaPlayer);
|
|
83
src/state-summary/state-card-media_player.ts
Normal file
83
src/state-summary/state-card-media_player.ts
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
import type { HassEntity } from "home-assistant-js-websocket";
|
||||||
|
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
||||||
|
import { customElement, property } from "lit/decorators";
|
||||||
|
import { computeStateDisplay } from "../common/entity/compute_state_display";
|
||||||
|
import "../components/entity/state-info";
|
||||||
|
import HassMediaPlayerEntity from "../util/hass-media-player-model";
|
||||||
|
import { HomeAssistant } from "../types";
|
||||||
|
import { haStyle } from "../resources/styles";
|
||||||
|
|
||||||
|
@customElement("state-card-media_player")
|
||||||
|
class StateCardMediaPlayer extends LitElement {
|
||||||
|
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||||
|
|
||||||
|
@property({ attribute: false }) public stateObj!: HassEntity;
|
||||||
|
|
||||||
|
@property({ type: Boolean }) public inDialog = false;
|
||||||
|
|
||||||
|
protected render(): TemplateResult {
|
||||||
|
const playerObj = new HassMediaPlayerEntity(this.hass, this.stateObj);
|
||||||
|
return html`
|
||||||
|
<div class="horizontal justified layout">
|
||||||
|
<state-info
|
||||||
|
.hass=${this.hass}
|
||||||
|
.stateObj=${this.stateObj}
|
||||||
|
.inDialog=${this.inDialog}
|
||||||
|
></state-info>
|
||||||
|
<div class="state">
|
||||||
|
<div class="main-text" take-height=${!playerObj.secondaryTitle}>
|
||||||
|
${this._computePrimaryText(this.hass.localize, playerObj)}
|
||||||
|
</div>
|
||||||
|
<div class="secondary-text">${playerObj.secondaryTitle}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _computePrimaryText(localize, playerObj) {
|
||||||
|
return (
|
||||||
|
playerObj.primaryTitle ||
|
||||||
|
computeStateDisplay(
|
||||||
|
localize,
|
||||||
|
playerObj.stateObj,
|
||||||
|
this.hass.locale,
|
||||||
|
this.hass.config,
|
||||||
|
this.hass.entities
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
static get styles(): CSSResultGroup {
|
||||||
|
return [
|
||||||
|
haStyle,
|
||||||
|
css`
|
||||||
|
:host {
|
||||||
|
line-height: 1.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.state {
|
||||||
|
margin-left: 16px;
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
.main-text {
|
||||||
|
color: var(--primary-text-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.main-text[take-height] {
|
||||||
|
line-height: 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.secondary-text {
|
||||||
|
color: var(--secondary-text-color);
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface HTMLElementTagNameMap {
|
||||||
|
"state-card-media_player": StateCardMediaPlayer;
|
||||||
|
}
|
||||||
|
}
|
@ -1,201 +0,0 @@
|
|||||||
import "@polymer/iron-flex-layout/iron-flex-layout-classes";
|
|
||||||
import { IronResizableBehavior } from "@polymer/iron-resizable-behavior/iron-resizable-behavior";
|
|
||||||
import { mixinBehaviors } from "@polymer/polymer/lib/legacy/class";
|
|
||||||
import { html } from "@polymer/polymer/lib/utils/html-tag";
|
|
||||||
/* eslint-plugin-disable lit */
|
|
||||||
import { PolymerElement } from "@polymer/polymer/polymer-element";
|
|
||||||
import "../components/entity/state-info";
|
|
||||||
import "../components/ha-slider";
|
|
||||||
import "../components/ha-textfield";
|
|
||||||
|
|
||||||
class StateCardNumber extends mixinBehaviors(
|
|
||||||
[IronResizableBehavior],
|
|
||||||
PolymerElement
|
|
||||||
) {
|
|
||||||
static get template() {
|
|
||||||
return html`
|
|
||||||
<style include="iron-flex iron-flex-alignment"></style>
|
|
||||||
<style>
|
|
||||||
ha-slider {
|
|
||||||
margin-left: auto;
|
|
||||||
flex-grow: 1;
|
|
||||||
}
|
|
||||||
.state {
|
|
||||||
@apply --paper-font-body1;
|
|
||||||
color: var(--primary-text-color);
|
|
||||||
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: end;
|
|
||||||
}
|
|
||||||
.sliderstate {
|
|
||||||
min-width: 45px;
|
|
||||||
}
|
|
||||||
[hidden] {
|
|
||||||
display: none !important;
|
|
||||||
}
|
|
||||||
ha-textfield {
|
|
||||||
text-align: right;
|
|
||||||
margin-left: auto;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<div class="horizontal justified layout" id="number_card">
|
|
||||||
${this.stateInfoTemplate}
|
|
||||||
<ha-slider
|
|
||||||
labeled
|
|
||||||
min="[[min]]"
|
|
||||||
max="[[max]]"
|
|
||||||
value="{{value}}"
|
|
||||||
step="[[step]]"
|
|
||||||
hidden="[[hiddenslider]]"
|
|
||||||
on-change="selectedValueChanged"
|
|
||||||
on-click="stopPropagation"
|
|
||||||
id="slider"
|
|
||||||
>
|
|
||||||
</ha-slider>
|
|
||||||
<ha-textfield
|
|
||||||
auto-validate=""
|
|
||||||
pattern="[0-9]+([\\.][0-9]+)?"
|
|
||||||
step="[[step]]"
|
|
||||||
min="[[min]]"
|
|
||||||
max="[[max]]"
|
|
||||||
value="[[value]]"
|
|
||||||
type="number"
|
|
||||||
on-input="onInput"
|
|
||||||
on-change="selectedValueChanged"
|
|
||||||
on-click="stopPropagation"
|
|
||||||
hidden="[[hiddenbox]]"
|
|
||||||
>
|
|
||||||
</ha-textfield>
|
|
||||||
<div class="state" hidden="[[hiddenbox]]">
|
|
||||||
[[stateObj.attributes.unit_of_measurement]]
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
id="sliderstate"
|
|
||||||
class="state sliderstate"
|
|
||||||
hidden="[[hiddenslider]]"
|
|
||||||
>
|
|
||||||
[[value]] [[stateObj.attributes.unit_of_measurement]]
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
|
|
||||||
static get stateInfoTemplate() {
|
|
||||||
return html`
|
|
||||||
<state-info
|
|
||||||
hass="[[hass]]"
|
|
||||||
state-obj="[[stateObj]]"
|
|
||||||
in-dialog="[[inDialog]]"
|
|
||||||
></state-info>
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
|
|
||||||
ready() {
|
|
||||||
super.ready();
|
|
||||||
if (typeof ResizeObserver === "function") {
|
|
||||||
const ro = new ResizeObserver((entries) => {
|
|
||||||
entries.forEach(() => {
|
|
||||||
this.hiddenState();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
ro.observe(this.$.number_card);
|
|
||||||
} else {
|
|
||||||
this.addEventListener("iron-resize", () => this.hiddenState());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static get properties() {
|
|
||||||
return {
|
|
||||||
hass: Object,
|
|
||||||
hiddenbox: {
|
|
||||||
type: Boolean,
|
|
||||||
value: true,
|
|
||||||
},
|
|
||||||
hiddenslider: {
|
|
||||||
type: Boolean,
|
|
||||||
value: true,
|
|
||||||
},
|
|
||||||
inDialog: {
|
|
||||||
type: Boolean,
|
|
||||||
value: false,
|
|
||||||
},
|
|
||||||
stateObj: {
|
|
||||||
type: Object,
|
|
||||||
observer: "stateObjectChanged",
|
|
||||||
},
|
|
||||||
min: {
|
|
||||||
type: Number,
|
|
||||||
value: 0,
|
|
||||||
},
|
|
||||||
max: {
|
|
||||||
type: Number,
|
|
||||||
value: 100,
|
|
||||||
},
|
|
||||||
maxlength: {
|
|
||||||
type: Number,
|
|
||||||
value: 3,
|
|
||||||
},
|
|
||||||
step: Number,
|
|
||||||
value: Number,
|
|
||||||
mode: String,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
hiddenState() {
|
|
||||||
if (this.mode !== "slider") return;
|
|
||||||
const sliderwidth = this.$.slider.offsetWidth;
|
|
||||||
if (sliderwidth < 100) {
|
|
||||||
this.$.sliderstate.hidden = true;
|
|
||||||
} else if (sliderwidth >= 145) {
|
|
||||||
this.$.sliderstate.hidden = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
stateObjectChanged(newVal) {
|
|
||||||
const prevMode = this.mode;
|
|
||||||
const min = Number(newVal.attributes.min);
|
|
||||||
const max = Number(newVal.attributes.max);
|
|
||||||
const step = Number(newVal.attributes.step);
|
|
||||||
const range = (max - min) / step;
|
|
||||||
|
|
||||||
this.setProperties({
|
|
||||||
min: min,
|
|
||||||
max: max,
|
|
||||||
step: step,
|
|
||||||
value: Number(newVal.state),
|
|
||||||
mode: String(newVal.attributes.mode),
|
|
||||||
maxlength: String(newVal.attributes.max).length,
|
|
||||||
hiddenbox:
|
|
||||||
newVal.attributes.mode === "slider" ||
|
|
||||||
(newVal.attributes.mode === "auto" && range <= 256),
|
|
||||||
hiddenslider:
|
|
||||||
newVal.attributes.mode === "box" ||
|
|
||||||
(newVal.attributes.mode === "auto" && range > 256),
|
|
||||||
});
|
|
||||||
if (this.mode === "slider" && prevMode !== "slider") {
|
|
||||||
this.hiddenState();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
onInput(ev) {
|
|
||||||
this.value = ev.target.value;
|
|
||||||
}
|
|
||||||
|
|
||||||
selectedValueChanged(ev) {
|
|
||||||
if (ev.target.value === Number(this.stateObj.state)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.hass.callService("number", "set_value", {
|
|
||||||
value: ev.target.value,
|
|
||||||
entity_id: this.stateObj.entity_id,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
stopPropagation(ev) {
|
|
||||||
ev.stopPropagation();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
customElements.define("state-card-number", StateCardNumber);
|
|
168
src/state-summary/state-card-number.ts
Normal file
168
src/state-summary/state-card-number.ts
Normal file
@ -0,0 +1,168 @@
|
|||||||
|
import type { HassEntity } from "home-assistant-js-websocket";
|
||||||
|
import { css, CSSResultGroup, html, LitElement } from "lit";
|
||||||
|
import { customElement, property } from "lit/decorators";
|
||||||
|
import "../components/entity/state-info";
|
||||||
|
import "../components/ha-slider";
|
||||||
|
import "../components/ha-textfield";
|
||||||
|
import { HomeAssistant } from "../types";
|
||||||
|
import { haStyle } from "../resources/styles";
|
||||||
|
import { loadPolyfillIfNeeded } from "../resources/resize-observer.polyfill";
|
||||||
|
import { computeRTLDirection } from "../common/util/compute_rtl";
|
||||||
|
import { isUnavailableState } from "../data/entity";
|
||||||
|
import { debounce } from "../common/util/debounce";
|
||||||
|
|
||||||
|
@customElement("state-card-number")
|
||||||
|
class StateCardNumber extends LitElement {
|
||||||
|
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||||
|
|
||||||
|
@property({ attribute: false }) public stateObj!: HassEntity;
|
||||||
|
|
||||||
|
@property({ type: Boolean }) public inDialog = false;
|
||||||
|
|
||||||
|
private _loaded?: boolean;
|
||||||
|
|
||||||
|
private _updated?: boolean;
|
||||||
|
|
||||||
|
private _resizeObserver?: ResizeObserver;
|
||||||
|
|
||||||
|
public connectedCallback(): void {
|
||||||
|
super.connectedCallback();
|
||||||
|
if (this._updated && !this._loaded) {
|
||||||
|
this._initialLoad();
|
||||||
|
}
|
||||||
|
this._attachObserver();
|
||||||
|
}
|
||||||
|
|
||||||
|
public disconnectedCallback(): void {
|
||||||
|
super.disconnectedCallback();
|
||||||
|
this._resizeObserver?.disconnect();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected firstUpdated(): void {
|
||||||
|
this._updated = true;
|
||||||
|
if (this.isConnected && !this._loaded) {
|
||||||
|
this._initialLoad();
|
||||||
|
}
|
||||||
|
this._attachObserver();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected render() {
|
||||||
|
const range = this.stateObj.attributes.max - this.stateObj.attributes.min;
|
||||||
|
|
||||||
|
return html`
|
||||||
|
<state-info
|
||||||
|
.hass=${this.hass}
|
||||||
|
.stateObj=${this.stateObj}
|
||||||
|
.inDialog=${this.inDialog}
|
||||||
|
></state-info>
|
||||||
|
${this.stateObj.attributes.mode === "slider" ||
|
||||||
|
(this.stateObj.attributes.mode === "auto" && range <= 256)
|
||||||
|
? html`
|
||||||
|
<div class="flex">
|
||||||
|
<ha-slider
|
||||||
|
labeled
|
||||||
|
.dir=${computeRTLDirection(this.hass)}
|
||||||
|
.disabled=${isUnavailableState(this.stateObj.state)}
|
||||||
|
.step=${Number(this.stateObj.attributes.step)}
|
||||||
|
.min=${Number(this.stateObj.attributes.min)}
|
||||||
|
.max=${Number(this.stateObj.attributes.max)}
|
||||||
|
.value=${this.stateObj.state}
|
||||||
|
@change=${this._selectedValueChanged}
|
||||||
|
>
|
||||||
|
</ha-slider>
|
||||||
|
<span class="state">
|
||||||
|
${this.hass.formatEntityState(this.stateObj)}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
`
|
||||||
|
: html` <div class="flex state">
|
||||||
|
<ha-textfield
|
||||||
|
.disabled=${isUnavailableState(this.stateObj.state)}
|
||||||
|
pattern="[0-9]+([\\.][0-9]+)?"
|
||||||
|
.step=${Number(this.stateObj.attributes.step)}
|
||||||
|
.min=${Number(this.stateObj.attributes.min)}
|
||||||
|
.max=${Number(this.stateObj.attributes.max)}
|
||||||
|
.value=${Number(this.stateObj.state).toString()}
|
||||||
|
.suffix=${this.stateObj.attributes.unit_of_measurement || ""}
|
||||||
|
type="number"
|
||||||
|
@change=${this._selectedValueChanged}
|
||||||
|
>
|
||||||
|
</ha-textfield>
|
||||||
|
</div>`}
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async _initialLoad(): Promise<void> {
|
||||||
|
this._loaded = true;
|
||||||
|
await this.updateComplete;
|
||||||
|
this._measureCard();
|
||||||
|
}
|
||||||
|
|
||||||
|
private _measureCard() {
|
||||||
|
if (!this.isConnected) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const element = this.shadowRoot!.querySelector(".state") as HTMLElement;
|
||||||
|
if (!element) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
element.hidden = this.clientWidth <= 300;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async _attachObserver(): Promise<void> {
|
||||||
|
if (!this._resizeObserver) {
|
||||||
|
await loadPolyfillIfNeeded();
|
||||||
|
this._resizeObserver = new ResizeObserver(
|
||||||
|
debounce(() => this._measureCard(), 250, false)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (this.isConnected) {
|
||||||
|
this._resizeObserver.observe(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async _selectedValueChanged(ev: Event) {
|
||||||
|
const value = (ev.target as HTMLInputElement).value;
|
||||||
|
if (value === this.stateObj.state) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
await this.hass.callService("number", "set_value", {
|
||||||
|
value: value,
|
||||||
|
entity_id: this.stateObj.entity_id,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
static get styles(): CSSResultGroup {
|
||||||
|
return [
|
||||||
|
haStyle,
|
||||||
|
css`
|
||||||
|
:host {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
.flex {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: flex-end;
|
||||||
|
flex-grow: 2;
|
||||||
|
}
|
||||||
|
.state {
|
||||||
|
min-width: 45px;
|
||||||
|
text-align: end;
|
||||||
|
}
|
||||||
|
ha-textfield {
|
||||||
|
text-align: end;
|
||||||
|
}
|
||||||
|
ha-slider {
|
||||||
|
width: 100%;
|
||||||
|
max-width: 200px;
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface HTMLElementTagNameMap {
|
||||||
|
"state-card-number": StateCardNumber;
|
||||||
|
}
|
||||||
|
}
|
@ -1,60 +0,0 @@
|
|||||||
import "@material/mwc-button";
|
|
||||||
import "@polymer/iron-flex-layout/iron-flex-layout-classes";
|
|
||||||
import { html } from "@polymer/polymer/lib/utils/html-tag";
|
|
||||||
/* eslint-plugin-disable lit */
|
|
||||||
import { PolymerElement } from "@polymer/polymer/polymer-element";
|
|
||||||
import "../components/entity/state-info";
|
|
||||||
import { activateScene } from "../data/scene";
|
|
||||||
import LocalizeMixin from "../mixins/localize-mixin";
|
|
||||||
|
|
||||||
/*
|
|
||||||
* @appliesMixin LocalizeMixin
|
|
||||||
*/
|
|
||||||
class StateCardScene extends LocalizeMixin(PolymerElement) {
|
|
||||||
static get template() {
|
|
||||||
return html`
|
|
||||||
<style include="iron-flex iron-flex-alignment"></style>
|
|
||||||
<style>
|
|
||||||
mwc-button {
|
|
||||||
top: 3px;
|
|
||||||
height: 37px;
|
|
||||||
margin-right: -0.57em;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<div class="horizontal justified layout">
|
|
||||||
${this.stateInfoTemplate}
|
|
||||||
<mwc-button on-click="_activateScene"
|
|
||||||
>[[localize('ui.card.scene.activate')]]</mwc-button
|
|
||||||
>
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
|
|
||||||
static get stateInfoTemplate() {
|
|
||||||
return html`
|
|
||||||
<state-info
|
|
||||||
hass="[[hass]]"
|
|
||||||
state-obj="[[stateObj]]"
|
|
||||||
in-dialog="[[inDialog]]"
|
|
||||||
></state-info>
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
|
|
||||||
static get properties() {
|
|
||||||
return {
|
|
||||||
hass: Object,
|
|
||||||
stateObj: Object,
|
|
||||||
inDialog: {
|
|
||||||
type: Boolean,
|
|
||||||
value: false,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
_activateScene(ev) {
|
|
||||||
ev.stopPropagation();
|
|
||||||
activateScene(this.hass, this.stateObj.entity_id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
customElements.define("state-card-scene", StateCardScene);
|
|
56
src/state-summary/state-card-scene.ts
Normal file
56
src/state-summary/state-card-scene.ts
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
import "@material/mwc-button";
|
||||||
|
import type { HassEntity } from "home-assistant-js-websocket";
|
||||||
|
import { css, CSSResultGroup, html, LitElement } from "lit";
|
||||||
|
import { customElement, property } from "lit/decorators";
|
||||||
|
import "../components/entity/state-info";
|
||||||
|
import { activateScene } from "../data/scene";
|
||||||
|
import { HomeAssistant } from "../types";
|
||||||
|
import { haStyle } from "../resources/styles";
|
||||||
|
|
||||||
|
@customElement("state-card-scene")
|
||||||
|
class StateCardScene extends LitElement {
|
||||||
|
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||||
|
|
||||||
|
@property({ attribute: false }) public stateObj!: HassEntity;
|
||||||
|
|
||||||
|
@property({ type: Boolean }) public inDialog = false;
|
||||||
|
|
||||||
|
protected render() {
|
||||||
|
return html`
|
||||||
|
<div class="horizontal justified layout">
|
||||||
|
<state-info
|
||||||
|
.hass=${this.hass}
|
||||||
|
.stateObj=${this.stateObj}
|
||||||
|
.inDialog=${this.inDialog}
|
||||||
|
></state-info>
|
||||||
|
<mwc-button @click=${this._activateScene}
|
||||||
|
>${this.hass.localize("ui.card.scene.activate")}</mwc-button
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _activateScene(ev) {
|
||||||
|
ev.stopPropagation();
|
||||||
|
activateScene(this.hass, this.stateObj.entity_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
static get styles(): CSSResultGroup {
|
||||||
|
return [
|
||||||
|
haStyle,
|
||||||
|
css`
|
||||||
|
mwc-button {
|
||||||
|
top: 3px;
|
||||||
|
height: 37px;
|
||||||
|
margin-right: -0.57em;
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface HTMLElementTagNameMap {
|
||||||
|
"state-card-scene": StateCardScene;
|
||||||
|
}
|
||||||
|
}
|
@ -10,7 +10,7 @@ import { haStyle } from "../resources/styles";
|
|||||||
import { HomeAssistant } from "../types";
|
import { HomeAssistant } from "../types";
|
||||||
|
|
||||||
@customElement("state-card-script")
|
@customElement("state-card-script")
|
||||||
export class StateCardScript extends LitElement {
|
class StateCardScript extends LitElement {
|
||||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||||
|
|
||||||
@property() public stateObj!: HassEntity;
|
@property() public stateObj!: HassEntity;
|
||||||
@ -69,3 +69,9 @@ export class StateCardScript extends LitElement {
|
|||||||
return haStyle;
|
return haStyle;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface HTMLElementTagNameMap {
|
||||||
|
"state-card-script": StateCardScript;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,97 +0,0 @@
|
|||||||
import "@polymer/iron-flex-layout/iron-flex-layout-classes";
|
|
||||||
import { html } from "@polymer/polymer/lib/utils/html-tag";
|
|
||||||
/* eslint-plugin-disable lit */
|
|
||||||
import { PolymerElement } from "@polymer/polymer/polymer-element";
|
|
||||||
import "../components/entity/state-info";
|
|
||||||
import { computeDisplayTimer, timerTimeRemaining } from "../data/timer";
|
|
||||||
|
|
||||||
class StateCardTimer extends PolymerElement {
|
|
||||||
static get template() {
|
|
||||||
return html`
|
|
||||||
<style include="iron-flex iron-flex-alignment"></style>
|
|
||||||
<style>
|
|
||||||
.state {
|
|
||||||
@apply --paper-font-body1;
|
|
||||||
color: var(--primary-text-color);
|
|
||||||
|
|
||||||
margin-left: 16px;
|
|
||||||
text-align: right;
|
|
||||||
line-height: 40px;
|
|
||||||
white-space: nowrap;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<div class="horizontal justified layout">
|
|
||||||
${this.stateInfoTemplate}
|
|
||||||
<div class="state">[[_displayState(timeRemaining, stateObj)]]</div>
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
|
|
||||||
static get stateInfoTemplate() {
|
|
||||||
return html`
|
|
||||||
<state-info
|
|
||||||
hass="[[hass]]"
|
|
||||||
state-obj="[[stateObj]]"
|
|
||||||
in-dialog="[[inDialog]]"
|
|
||||||
></state-info>
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
|
|
||||||
static get properties() {
|
|
||||||
return {
|
|
||||||
hass: Object,
|
|
||||||
stateObj: {
|
|
||||||
type: Object,
|
|
||||||
observer: "stateObjChanged",
|
|
||||||
},
|
|
||||||
timeRemaining: Number,
|
|
||||||
inDialog: {
|
|
||||||
type: Boolean,
|
|
||||||
value: false,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
connectedCallback() {
|
|
||||||
super.connectedCallback();
|
|
||||||
this.startInterval(this.stateObj);
|
|
||||||
}
|
|
||||||
|
|
||||||
disconnectedCallback() {
|
|
||||||
super.disconnectedCallback();
|
|
||||||
this.clearInterval();
|
|
||||||
}
|
|
||||||
|
|
||||||
stateObjChanged(stateObj) {
|
|
||||||
this.startInterval(stateObj);
|
|
||||||
}
|
|
||||||
|
|
||||||
clearInterval() {
|
|
||||||
if (this._updateRemaining) {
|
|
||||||
clearInterval(this._updateRemaining);
|
|
||||||
this._updateRemaining = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
startInterval(stateObj) {
|
|
||||||
this.clearInterval();
|
|
||||||
this.calculateRemaining(stateObj);
|
|
||||||
|
|
||||||
if (stateObj.state === "active") {
|
|
||||||
this._updateRemaining = setInterval(
|
|
||||||
() => this.calculateRemaining(this.stateObj),
|
|
||||||
1000
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
calculateRemaining(stateObj) {
|
|
||||||
this.timeRemaining = timerTimeRemaining(stateObj);
|
|
||||||
}
|
|
||||||
|
|
||||||
_displayState(timeRemaining, stateObj) {
|
|
||||||
return computeDisplayTimer(this.hass, stateObj, timeRemaining);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
customElements.define("state-card-timer", StateCardTimer);
|
|
108
src/state-summary/state-card-timer.ts
Normal file
108
src/state-summary/state-card-timer.ts
Normal file
@ -0,0 +1,108 @@
|
|||||||
|
import type { HassEntity } from "home-assistant-js-websocket";
|
||||||
|
import {
|
||||||
|
css,
|
||||||
|
CSSResultGroup,
|
||||||
|
html,
|
||||||
|
LitElement,
|
||||||
|
PropertyValues,
|
||||||
|
TemplateResult,
|
||||||
|
} from "lit";
|
||||||
|
import { customElement, property } from "lit/decorators";
|
||||||
|
import "../components/entity/state-info";
|
||||||
|
import { computeDisplayTimer, timerTimeRemaining } from "../data/timer";
|
||||||
|
import { HomeAssistant } from "../types";
|
||||||
|
import { haStyle } from "../resources/styles";
|
||||||
|
|
||||||
|
@customElement("state-card-timer")
|
||||||
|
class StateCardTimer extends LitElement {
|
||||||
|
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||||
|
|
||||||
|
@property({ attribute: false }) public stateObj!: HassEntity;
|
||||||
|
|
||||||
|
@property({ type: Boolean }) public inDialog = false;
|
||||||
|
|
||||||
|
@property() public timeRemaining?: number;
|
||||||
|
|
||||||
|
private _updateRemaining: any;
|
||||||
|
|
||||||
|
protected render(): TemplateResult {
|
||||||
|
return html`
|
||||||
|
<div class="horizontal justified layout">
|
||||||
|
<state-info
|
||||||
|
.hass=${this.hass}
|
||||||
|
.stateObj=${this.stateObj}
|
||||||
|
.inDialog=${this.inDialog}
|
||||||
|
></state-info>
|
||||||
|
<div class="state">
|
||||||
|
${this._displayState(this.timeRemaining, this.stateObj)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
connectedCallback() {
|
||||||
|
super.connectedCallback();
|
||||||
|
this._startInterval(this.stateObj);
|
||||||
|
}
|
||||||
|
|
||||||
|
disconnectedCallback() {
|
||||||
|
super.disconnectedCallback();
|
||||||
|
this._clearInterval();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected willUpdate(changedProp: PropertyValues): void {
|
||||||
|
super.willUpdate(changedProp);
|
||||||
|
if (changedProp.has("stateObj")) {
|
||||||
|
this._startInterval(this.stateObj);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private _clearInterval() {
|
||||||
|
if (this._updateRemaining) {
|
||||||
|
clearInterval(this._updateRemaining);
|
||||||
|
this._updateRemaining = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private _startInterval(stateObj) {
|
||||||
|
this._clearInterval();
|
||||||
|
this._calculateRemaining(stateObj);
|
||||||
|
|
||||||
|
if (stateObj.state === "active") {
|
||||||
|
this._updateRemaining = setInterval(
|
||||||
|
() => this._calculateRemaining(this.stateObj),
|
||||||
|
1000
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private _calculateRemaining(stateObj) {
|
||||||
|
this.timeRemaining = timerTimeRemaining(stateObj);
|
||||||
|
}
|
||||||
|
|
||||||
|
private _displayState(timeRemaining, stateObj) {
|
||||||
|
return computeDisplayTimer(this.hass, stateObj, timeRemaining);
|
||||||
|
}
|
||||||
|
|
||||||
|
static get styles(): CSSResultGroup {
|
||||||
|
return [
|
||||||
|
haStyle,
|
||||||
|
css`
|
||||||
|
.state {
|
||||||
|
color: var(--primary-text-color);
|
||||||
|
|
||||||
|
margin-left: 16px;
|
||||||
|
text-align: right;
|
||||||
|
line-height: 40px;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface HTMLElementTagNameMap {
|
||||||
|
"state-card-timer": StateCardTimer;
|
||||||
|
}
|
||||||
|
}
|
@ -1,50 +0,0 @@
|
|||||||
import "@polymer/iron-flex-layout/iron-flex-layout-classes";
|
|
||||||
import { html } from "@polymer/polymer/lib/utils/html-tag";
|
|
||||||
/* eslint-plugin-disable lit */
|
|
||||||
import { PolymerElement } from "@polymer/polymer/polymer-element";
|
|
||||||
import "../components/entity/ha-entity-toggle";
|
|
||||||
import "../components/entity/state-info";
|
|
||||||
|
|
||||||
class StateCardToggle extends PolymerElement {
|
|
||||||
static get template() {
|
|
||||||
return html`
|
|
||||||
<style include="iron-flex iron-flex-alignment"></style>
|
|
||||||
<style>
|
|
||||||
ha-entity-toggle {
|
|
||||||
margin: -4px -16px -4px 0;
|
|
||||||
padding: 4px 16px;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<div class="horizontal justified layout">
|
|
||||||
${this.stateInfoTemplate}
|
|
||||||
<ha-entity-toggle
|
|
||||||
state-obj="[[stateObj]]"
|
|
||||||
hass="[[hass]]"
|
|
||||||
></ha-entity-toggle>
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
|
|
||||||
static get stateInfoTemplate() {
|
|
||||||
return html`
|
|
||||||
<state-info
|
|
||||||
hass="[[hass]]"
|
|
||||||
state-obj="[[stateObj]]"
|
|
||||||
in-dialog="[[inDialog]]"
|
|
||||||
></state-info>
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
|
|
||||||
static get properties() {
|
|
||||||
return {
|
|
||||||
hass: Object,
|
|
||||||
stateObj: Object,
|
|
||||||
inDialog: {
|
|
||||||
type: Boolean,
|
|
||||||
value: false,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
customElements.define("state-card-toggle", StateCardToggle);
|
|
51
src/state-summary/state-card-toggle.ts
Normal file
51
src/state-summary/state-card-toggle.ts
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
import type { HassEntity } from "home-assistant-js-websocket";
|
||||||
|
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
||||||
|
import { customElement, property } from "lit/decorators";
|
||||||
|
import "../components/entity/ha-entity-toggle";
|
||||||
|
import "../components/entity/state-info";
|
||||||
|
import { HomeAssistant } from "../types";
|
||||||
|
import { haStyle } from "../resources/styles";
|
||||||
|
|
||||||
|
@customElement("state-card-toggle")
|
||||||
|
class StateCardToggle extends LitElement {
|
||||||
|
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||||
|
|
||||||
|
@property({ attribute: false }) public stateObj!: HassEntity;
|
||||||
|
|
||||||
|
@property({ type: Boolean }) public inDialog = false;
|
||||||
|
|
||||||
|
protected render(): TemplateResult {
|
||||||
|
return html`
|
||||||
|
<div class="horizontal justified layout">
|
||||||
|
<state-info
|
||||||
|
.hass=${this.hass}
|
||||||
|
.stateObj=${this.stateObj}
|
||||||
|
.inDialog=${this.inDialog}
|
||||||
|
>
|
||||||
|
</state-info>
|
||||||
|
<ha-entity-toggle
|
||||||
|
.hass=${this.hass}
|
||||||
|
.stateObj=${this.stateObj}
|
||||||
|
></ha-entity-toggle>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
static get styles(): CSSResultGroup {
|
||||||
|
return [
|
||||||
|
haStyle,
|
||||||
|
css`
|
||||||
|
ha-entity-toggle {
|
||||||
|
margin: -4px -16px -4px 0;
|
||||||
|
padding: 4px 16px;
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface HTMLElementTagNameMap {
|
||||||
|
"state-card-toggle": StateCardToggle;
|
||||||
|
}
|
||||||
|
}
|
@ -1,44 +0,0 @@
|
|||||||
import "@polymer/iron-flex-layout/iron-flex-layout-classes";
|
|
||||||
import { html } from "@polymer/polymer/lib/utils/html-tag";
|
|
||||||
/* eslint-plugin-disable lit */
|
|
||||||
import { PolymerElement } from "@polymer/polymer/polymer-element";
|
|
||||||
import "../components/entity/state-info";
|
|
||||||
import "../components/ha-vacuum-state";
|
|
||||||
|
|
||||||
class StateCardVacuum extends PolymerElement {
|
|
||||||
static get template() {
|
|
||||||
return html`
|
|
||||||
<style include="iron-flex iron-flex-alignment"></style>
|
|
||||||
|
|
||||||
<div class="horizontal justified layout">
|
|
||||||
${this.stateInfoTemplate}
|
|
||||||
<ha-vacuum-state
|
|
||||||
hass="[[hass]]"
|
|
||||||
state-obj="[[stateObj]]"
|
|
||||||
></ha-vacuum-state>
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
|
|
||||||
static get stateInfoTemplate() {
|
|
||||||
return html`
|
|
||||||
<state-info
|
|
||||||
hass="[[hass]]"
|
|
||||||
state-obj="[[stateObj]]"
|
|
||||||
in-dialog="[[inDialog]]"
|
|
||||||
></state-info>
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
|
|
||||||
static get properties() {
|
|
||||||
return {
|
|
||||||
hass: Object,
|
|
||||||
stateObj: Object,
|
|
||||||
inDialog: {
|
|
||||||
type: Boolean,
|
|
||||||
value: false,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
customElements.define("state-card-vacuum", StateCardVacuum);
|
|
44
src/state-summary/state-card-vacuum.ts
Normal file
44
src/state-summary/state-card-vacuum.ts
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
import type { HassEntity } from "home-assistant-js-websocket";
|
||||||
|
import { CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
||||||
|
import { customElement, property } from "lit/decorators";
|
||||||
|
import "../components/entity/state-info";
|
||||||
|
import "../components/ha-vacuum-state";
|
||||||
|
import { HomeAssistant } from "../types";
|
||||||
|
import { haStyle } from "../resources/styles";
|
||||||
|
|
||||||
|
@customElement("state-card-vacuum")
|
||||||
|
class StateCardVacuum extends LitElement {
|
||||||
|
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||||
|
|
||||||
|
@property({ attribute: false }) public stateObj!: HassEntity;
|
||||||
|
|
||||||
|
@property({ type: Boolean }) public inDialog = false;
|
||||||
|
|
||||||
|
protected render(): TemplateResult {
|
||||||
|
return html`
|
||||||
|
<div class="horizontal justified layout">
|
||||||
|
<state-info
|
||||||
|
.hass=${this.hass}
|
||||||
|
.stateObj=${this.stateObj}
|
||||||
|
.inDialog=${this.inDialog}
|
||||||
|
>
|
||||||
|
</state-info>
|
||||||
|
|
||||||
|
<ha-vacuum-state
|
||||||
|
.hass=${this.hass}
|
||||||
|
.stateObj=${this.stateObj}
|
||||||
|
></ha-vacuum-state>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
static get styles(): CSSResultGroup {
|
||||||
|
return [haStyle];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface HTMLElementTagNameMap {
|
||||||
|
"state-card-vacuum": StateCardVacuum;
|
||||||
|
}
|
||||||
|
}
|
@ -1,55 +0,0 @@
|
|||||||
import "@polymer/iron-flex-layout/iron-flex-layout-classes";
|
|
||||||
import { html } from "@polymer/polymer/lib/utils/html-tag";
|
|
||||||
/* eslint-plugin-disable lit */
|
|
||||||
import { PolymerElement } from "@polymer/polymer/polymer-element";
|
|
||||||
import "../components/entity/state-info";
|
|
||||||
import "../components/ha-water_heater-state";
|
|
||||||
|
|
||||||
class StateCardWaterHeater extends PolymerElement {
|
|
||||||
static get template() {
|
|
||||||
return html`
|
|
||||||
<style include="iron-flex iron-flex-alignment"></style>
|
|
||||||
<style>
|
|
||||||
:host {
|
|
||||||
@apply --paper-font-body1;
|
|
||||||
line-height: 1.5;
|
|
||||||
}
|
|
||||||
|
|
||||||
ha-water_heater-state {
|
|
||||||
margin-left: 16px;
|
|
||||||
text-align: right;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<div class="horizontal justified layout">
|
|
||||||
${this.stateInfoTemplate}
|
|
||||||
<ha-water_heater-state
|
|
||||||
hass="[[hass]]"
|
|
||||||
state-obj="[[stateObj]]"
|
|
||||||
></ha-water_heater-state>
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
|
|
||||||
static get stateInfoTemplate() {
|
|
||||||
return html`
|
|
||||||
<state-info
|
|
||||||
hass="[[hass]]"
|
|
||||||
state-obj="[[stateObj]]"
|
|
||||||
in-dialog="[[inDialog]]"
|
|
||||||
></state-info>
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
|
|
||||||
static get properties() {
|
|
||||||
return {
|
|
||||||
hass: Object,
|
|
||||||
stateObj: Object,
|
|
||||||
inDialog: {
|
|
||||||
type: Boolean,
|
|
||||||
value: false,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
customElements.define("state-card-water_heater", StateCardWaterHeater);
|
|
55
src/state-summary/state-card-water_heater.ts
Normal file
55
src/state-summary/state-card-water_heater.ts
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
import type { HassEntity } from "home-assistant-js-websocket";
|
||||||
|
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
||||||
|
import { customElement, property } from "lit/decorators";
|
||||||
|
import "../components/entity/state-info";
|
||||||
|
import "../components/ha-water_heater-state";
|
||||||
|
import { haStyle } from "../resources/styles";
|
||||||
|
import { HomeAssistant } from "../types";
|
||||||
|
|
||||||
|
@customElement("state-card-water_heater")
|
||||||
|
class StateCardWaterHeater extends LitElement {
|
||||||
|
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||||
|
|
||||||
|
@property({ attribute: false }) public stateObj!: HassEntity;
|
||||||
|
|
||||||
|
@property({ type: Boolean }) public inDialog = false;
|
||||||
|
|
||||||
|
protected render(): TemplateResult {
|
||||||
|
return html`
|
||||||
|
<div class="horizontal justified layout">
|
||||||
|
<state-info
|
||||||
|
.hass=${this.hass}
|
||||||
|
.stateObj=${this.stateObj}
|
||||||
|
.inDialog=${this.inDialog}
|
||||||
|
>
|
||||||
|
</state-info>
|
||||||
|
<ha-water_heater-state
|
||||||
|
.hass=${this.hass}
|
||||||
|
.stateObj=${this.stateObj}
|
||||||
|
></ha-water_heater-state>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
static get styles(): CSSResultGroup {
|
||||||
|
return [
|
||||||
|
haStyle,
|
||||||
|
css`
|
||||||
|
:host {
|
||||||
|
line-height: 1.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
ha-water_heater-state {
|
||||||
|
margin-left: 16px;
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface HTMLElementTagNameMap {
|
||||||
|
"state-card-water_heater": StateCardWaterHeater;
|
||||||
|
}
|
||||||
|
}
|
@ -1,12 +1,19 @@
|
|||||||
|
import { HassEntity } from "home-assistant-js-websocket";
|
||||||
import { supportsFeature } from "../common/entity/supports-feature";
|
import { supportsFeature } from "../common/entity/supports-feature";
|
||||||
import { cleanupMediaTitle } from "../data/media-player";
|
import { cleanupMediaTitle } from "../data/media-player";
|
||||||
|
import { HomeAssistant } from "../types";
|
||||||
|
|
||||||
export default class MediaPlayerEntity {
|
export default class MediaPlayerEntity {
|
||||||
constructor(hass, stateObj) {
|
public hass: HomeAssistant;
|
||||||
|
|
||||||
|
public stateObj: HassEntity;
|
||||||
|
|
||||||
|
private _attr: { [key: string]: any };
|
||||||
|
|
||||||
|
constructor(hass: HomeAssistant, stateObj: HassEntity) {
|
||||||
this.hass = hass;
|
this.hass = hass;
|
||||||
this.stateObj = stateObj;
|
this.stateObj = stateObj;
|
||||||
this._attr = stateObj.attributes;
|
this._attr = stateObj.attributes;
|
||||||
this._feat = this._attr.supported_features;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
get isOff() {
|
get isOff() {
|
||||||
@ -219,7 +226,7 @@ export default class MediaPlayerEntity {
|
|||||||
|
|
||||||
// helper method
|
// helper method
|
||||||
|
|
||||||
callService(service, data = {}) {
|
callService(service, data: any = {}) {
|
||||||
data.entity_id = this.stateObj.entity_id;
|
data.entity_id = this.stateObj.entity_id;
|
||||||
this.hass.callService("media_player", service, data);
|
this.hass.callService("media_player", service, data);
|
||||||
}
|
}
|
Loading…
x
Reference in New Issue
Block a user