mirror of
https://github.com/home-assistant/frontend.git
synced 2026-08-23 15:18:07 +00:00
Compare commits
3
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c22d8ee669 | ||
|
|
183dc3cfbe | ||
|
|
e3bbe0e93b |
@@ -99,7 +99,6 @@ const findDifferentiator = (curString, prevString) => {
|
||||
|
||||
gulp.task("gen-icons-json", (done) => {
|
||||
const meta = getMeta();
|
||||
|
||||
const metaAndRemoved = addRemovedMeta(meta);
|
||||
const split = splitBySize(metaAndRemoved);
|
||||
|
||||
@@ -139,9 +138,11 @@ gulp.task("gen-icons-json", (done) => {
|
||||
JSON.stringify({ version: package.version, parts })
|
||||
);
|
||||
|
||||
const orderedMeta = orderMeta(meta);
|
||||
|
||||
fs.writeFileSync(
|
||||
path.resolve(OUTPUT_DIR, "iconList.json"),
|
||||
JSON.stringify(orderMeta(meta).map((icon) => icon.name))
|
||||
JSON.stringify(orderedMeta.map((icon) => icon.name))
|
||||
);
|
||||
|
||||
done();
|
||||
|
||||
@@ -1,91 +0,0 @@
|
||||
/* eslint-disable lit/no-template-arrow */
|
||||
import { LitElement, TemplateResult, html } from "lit";
|
||||
import { customElement, state } from "lit/decorators";
|
||||
import { provideHass } from "../../../src/fake_data/provide_hass";
|
||||
import type { HomeAssistant } from "../../../src/types";
|
||||
import "../components/demo-black-white-row";
|
||||
import { mockEntityRegistry } from "../../../demo/src/stubs/entity_registry";
|
||||
import { mockDeviceRegistry } from "../../../demo/src/stubs/device_registry";
|
||||
import { mockAreaRegistry } from "../../../demo/src/stubs/area_registry";
|
||||
import { mockHassioSupervisor } from "../../../demo/src/stubs/hassio_supervisor";
|
||||
import "../../../src/panels/config/automation/action/ha-automation-action";
|
||||
import { HaChooseAction } from "../../../src/panels/config/automation/action/types/ha-automation-action-choose";
|
||||
import { HaDelayAction } from "../../../src/panels/config/automation/action/types/ha-automation-action-delay";
|
||||
import { HaDeviceAction } from "../../../src/panels/config/automation/action/types/ha-automation-action-device_id";
|
||||
import { HaEventAction } from "../../../src/panels/config/automation/action/types/ha-automation-action-event";
|
||||
import { HaRepeatAction } from "../../../src/panels/config/automation/action/types/ha-automation-action-repeat";
|
||||
import { HaSceneAction } from "../../../src/panels/config/automation/action/types/ha-automation-action-scene";
|
||||
import { HaServiceAction } from "../../../src/panels/config/automation/action/types/ha-automation-action-service";
|
||||
import { HaWaitForTriggerAction } from "../../../src/panels/config/automation/action/types/ha-automation-action-wait_for_trigger";
|
||||
import { HaWaitAction } from "../../../src/panels/config/automation/action/types/ha-automation-action-wait_template";
|
||||
import { Action } from "../../../src/data/script";
|
||||
import { HaConditionAction } from "../../../src/panels/config/automation/action/types/ha-automation-action-condition";
|
||||
|
||||
const SCHEMAS: { name: string; actions: Action[] }[] = [
|
||||
{ name: "Event", actions: [HaEventAction.defaultConfig] },
|
||||
{ name: "Device", actions: [HaDeviceAction.defaultConfig] },
|
||||
{ name: "Service", actions: [HaServiceAction.defaultConfig] },
|
||||
{ name: "Condition", actions: [HaConditionAction.defaultConfig] },
|
||||
{ name: "Delay", actions: [HaDelayAction.defaultConfig] },
|
||||
{ name: "Scene", actions: [HaSceneAction.defaultConfig] },
|
||||
{ name: "Wait", actions: [HaWaitAction.defaultConfig] },
|
||||
{ name: "WaitForTrigger", actions: [HaWaitForTriggerAction.defaultConfig] },
|
||||
{ name: "Repeat", actions: [HaRepeatAction.defaultConfig] },
|
||||
{ name: "Choose", actions: [HaChooseAction.defaultConfig] },
|
||||
{ name: "Variables", actions: [{ variables: { hello: "1" } }] },
|
||||
];
|
||||
|
||||
@customElement("demo-automation-editor-action")
|
||||
class DemoHaAutomationEditorAction extends LitElement {
|
||||
@state() private hass!: HomeAssistant;
|
||||
|
||||
private data: any = SCHEMAS.map((info) => info.actions);
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
const hass = provideHass(this);
|
||||
hass.updateTranslations(null, "en");
|
||||
hass.updateTranslations("config", "en");
|
||||
mockEntityRegistry(hass);
|
||||
mockDeviceRegistry(hass);
|
||||
mockAreaRegistry(hass);
|
||||
mockHassioSupervisor(hass);
|
||||
}
|
||||
|
||||
protected render(): TemplateResult {
|
||||
const valueChanged = (ev) => {
|
||||
const sampleIdx = ev.target.sampleIdx;
|
||||
this.data[sampleIdx] = ev.detail.value;
|
||||
this.requestUpdate();
|
||||
};
|
||||
return html`
|
||||
${SCHEMAS.map(
|
||||
(info, sampleIdx) => html`
|
||||
<demo-black-white-row
|
||||
.title=${info.name}
|
||||
.value=${this.data[sampleIdx]}
|
||||
>
|
||||
${["light", "dark"].map(
|
||||
(slot) =>
|
||||
html`
|
||||
<ha-automation-action
|
||||
slot=${slot}
|
||||
.hass=${this.hass}
|
||||
.actions=${this.data[sampleIdx]}
|
||||
.sampleIdx=${sampleIdx}
|
||||
@value-changed=${valueChanged}
|
||||
></ha-automation-action>
|
||||
`
|
||||
)}
|
||||
</demo-black-white-row>
|
||||
`
|
||||
)}
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"demo-ha-automation-editor-action": DemoHaAutomationEditorAction;
|
||||
}
|
||||
}
|
||||
@@ -1,127 +0,0 @@
|
||||
/* eslint-disable lit/no-template-arrow */
|
||||
import { LitElement, TemplateResult, html } from "lit";
|
||||
import { customElement, state } from "lit/decorators";
|
||||
import { provideHass } from "../../../src/fake_data/provide_hass";
|
||||
import type { HomeAssistant } from "../../../src/types";
|
||||
import "../components/demo-black-white-row";
|
||||
import { mockEntityRegistry } from "../../../demo/src/stubs/entity_registry";
|
||||
import { mockDeviceRegistry } from "../../../demo/src/stubs/device_registry";
|
||||
import { mockAreaRegistry } from "../../../demo/src/stubs/area_registry";
|
||||
import { mockHassioSupervisor } from "../../../demo/src/stubs/hassio_supervisor";
|
||||
import type { Condition } from "../../../src/data/automation";
|
||||
import "../../../src/panels/config/automation/condition/ha-automation-condition";
|
||||
import { HaDeviceCondition } from "../../../src/panels/config/automation/condition/types/ha-automation-condition-device";
|
||||
import { HaLogicalCondition } from "../../../src/panels/config/automation/condition/types/ha-automation-condition-logical";
|
||||
import HaNumericStateCondition from "../../../src/panels/config/automation/condition/types/ha-automation-condition-numeric_state";
|
||||
import { HaStateCondition } from "../../../src/panels/config/automation/condition/types/ha-automation-condition-state";
|
||||
import { HaSunCondition } from "../../../src/panels/config/automation/condition/types/ha-automation-condition-sun";
|
||||
import { HaTemplateCondition } from "../../../src/panels/config/automation/condition/types/ha-automation-condition-template";
|
||||
import { HaTimeCondition } from "../../../src/panels/config/automation/condition/types/ha-automation-condition-time";
|
||||
import { HaTriggerCondition } from "../../../src/panels/config/automation/condition/types/ha-automation-condition-trigger";
|
||||
import { HaZoneCondition } from "../../../src/panels/config/automation/condition/types/ha-automation-condition-zone";
|
||||
|
||||
const SCHEMAS: { name: string; conditions: Condition[] }[] = [
|
||||
{
|
||||
name: "State",
|
||||
conditions: [{ condition: "state", ...HaStateCondition.defaultConfig }],
|
||||
},
|
||||
{
|
||||
name: "Numeric State",
|
||||
conditions: [
|
||||
{ condition: "numeric_state", ...HaNumericStateCondition.defaultConfig },
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Sun",
|
||||
conditions: [{ condition: "sun", ...HaSunCondition.defaultConfig }],
|
||||
},
|
||||
{
|
||||
name: "Zone",
|
||||
conditions: [{ condition: "zone", ...HaZoneCondition.defaultConfig }],
|
||||
},
|
||||
{
|
||||
name: "Time",
|
||||
conditions: [{ condition: "time", ...HaTimeCondition.defaultConfig }],
|
||||
},
|
||||
{
|
||||
name: "Template",
|
||||
conditions: [
|
||||
{ condition: "template", ...HaTemplateCondition.defaultConfig },
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Device",
|
||||
conditions: [{ condition: "device", ...HaDeviceCondition.defaultConfig }],
|
||||
},
|
||||
{
|
||||
name: "And",
|
||||
conditions: [{ condition: "and", ...HaLogicalCondition.defaultConfig }],
|
||||
},
|
||||
{
|
||||
name: "Or",
|
||||
conditions: [{ condition: "or", ...HaLogicalCondition.defaultConfig }],
|
||||
},
|
||||
{
|
||||
name: "Not",
|
||||
conditions: [{ condition: "not", ...HaLogicalCondition.defaultConfig }],
|
||||
},
|
||||
{
|
||||
name: "Trigger",
|
||||
conditions: [{ condition: "trigger", ...HaTriggerCondition.defaultConfig }],
|
||||
},
|
||||
];
|
||||
|
||||
@customElement("demo-automation-editor-condition")
|
||||
class DemoHaAutomationEditorCondition extends LitElement {
|
||||
@state() private hass!: HomeAssistant;
|
||||
|
||||
private data: any = SCHEMAS.map((info) => info.conditions);
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
const hass = provideHass(this);
|
||||
hass.updateTranslations(null, "en");
|
||||
hass.updateTranslations("config", "en");
|
||||
mockEntityRegistry(hass);
|
||||
mockDeviceRegistry(hass);
|
||||
mockAreaRegistry(hass);
|
||||
mockHassioSupervisor(hass);
|
||||
}
|
||||
|
||||
protected render(): TemplateResult {
|
||||
const valueChanged = (ev) => {
|
||||
const sampleIdx = ev.target.sampleIdx;
|
||||
this.data[sampleIdx] = ev.detail.value;
|
||||
this.requestUpdate();
|
||||
};
|
||||
return html`
|
||||
${SCHEMAS.map(
|
||||
(info, sampleIdx) => html`
|
||||
<demo-black-white-row
|
||||
.title=${info.name}
|
||||
.value=${this.data[sampleIdx]}
|
||||
>
|
||||
${["light", "dark"].map(
|
||||
(slot) =>
|
||||
html`
|
||||
<ha-automation-condition
|
||||
slot=${slot}
|
||||
.hass=${this.hass}
|
||||
.conditions=${this.data[sampleIdx]}
|
||||
.sampleIdx=${sampleIdx}
|
||||
@value-changed=${valueChanged}
|
||||
></ha-automation-condition>
|
||||
`
|
||||
)}
|
||||
</demo-black-white-row>
|
||||
`
|
||||
)}
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"demo-ha-automation-editor-condition": DemoHaAutomationEditorCondition;
|
||||
}
|
||||
}
|
||||
@@ -1,159 +0,0 @@
|
||||
/* eslint-disable lit/no-template-arrow */
|
||||
import { LitElement, TemplateResult, html } from "lit";
|
||||
import { customElement, state } from "lit/decorators";
|
||||
import { provideHass } from "../../../src/fake_data/provide_hass";
|
||||
import type { HomeAssistant } from "../../../src/types";
|
||||
import "../components/demo-black-white-row";
|
||||
import { mockEntityRegistry } from "../../../demo/src/stubs/entity_registry";
|
||||
import { mockDeviceRegistry } from "../../../demo/src/stubs/device_registry";
|
||||
import { mockAreaRegistry } from "../../../demo/src/stubs/area_registry";
|
||||
import { mockHassioSupervisor } from "../../../demo/src/stubs/hassio_supervisor";
|
||||
import type { Trigger } from "../../../src/data/automation";
|
||||
import { HaGeolocationTrigger } from "../../../src/panels/config/automation/trigger/types/ha-automation-trigger-geo_location";
|
||||
import { HaEventTrigger } from "../../../src/panels/config/automation/trigger/types/ha-automation-trigger-event";
|
||||
import { HaHassTrigger } from "../../../src/panels/config/automation/trigger/types/ha-automation-trigger-homeassistant";
|
||||
import { HaNumericStateTrigger } from "../../../src/panels/config/automation/trigger/types/ha-automation-trigger-numeric_state";
|
||||
import { HaSunTrigger } from "../../../src/panels/config/automation/trigger/types/ha-automation-trigger-sun";
|
||||
import { HaTagTrigger } from "../../../src/panels/config/automation/trigger/types/ha-automation-trigger-tag";
|
||||
import { HaTemplateTrigger } from "../../../src/panels/config/automation/trigger/types/ha-automation-trigger-template";
|
||||
import { HaTimeTrigger } from "../../../src/panels/config/automation/trigger/types/ha-automation-trigger-time";
|
||||
import { HaTimePatternTrigger } from "../../../src/panels/config/automation/trigger/types/ha-automation-trigger-time_pattern";
|
||||
import { HaWebhookTrigger } from "../../../src/panels/config/automation/trigger/types/ha-automation-trigger-webhook";
|
||||
import { HaZoneTrigger } from "../../../src/panels/config/automation/trigger/types/ha-automation-trigger-zone";
|
||||
import { HaDeviceTrigger } from "../../../src/panels/config/automation/trigger/types/ha-automation-trigger-device";
|
||||
import { HaStateTrigger } from "../../../src/panels/config/automation/trigger/types/ha-automation-trigger-state";
|
||||
import { HaMQTTTrigger } from "../../../src/panels/config/automation/trigger/types/ha-automation-trigger-mqtt";
|
||||
import "../../../src/panels/config/automation/trigger/ha-automation-trigger";
|
||||
|
||||
const SCHEMAS: { name: string; triggers: Trigger[] }[] = [
|
||||
{
|
||||
name: "State",
|
||||
triggers: [{ platform: "state", ...HaStateTrigger.defaultConfig }],
|
||||
},
|
||||
|
||||
{
|
||||
name: "MQTT",
|
||||
triggers: [{ platform: "mqtt", ...HaMQTTTrigger.defaultConfig }],
|
||||
},
|
||||
|
||||
{
|
||||
name: "GeoLocation",
|
||||
triggers: [
|
||||
{ platform: "geo_location", ...HaGeolocationTrigger.defaultConfig },
|
||||
],
|
||||
},
|
||||
|
||||
{
|
||||
name: "Home Assistant",
|
||||
triggers: [{ platform: "homeassistant", ...HaHassTrigger.defaultConfig }],
|
||||
},
|
||||
|
||||
{
|
||||
name: "Numeric State",
|
||||
triggers: [
|
||||
{ platform: "numeric_state", ...HaNumericStateTrigger.defaultConfig },
|
||||
],
|
||||
},
|
||||
|
||||
{
|
||||
name: "Sun",
|
||||
triggers: [{ platform: "sun", ...HaSunTrigger.defaultConfig }],
|
||||
},
|
||||
|
||||
{
|
||||
name: "Time Pattern",
|
||||
triggers: [
|
||||
{ platform: "time_pattern", ...HaTimePatternTrigger.defaultConfig },
|
||||
],
|
||||
},
|
||||
|
||||
{
|
||||
name: "Webhook",
|
||||
triggers: [{ platform: "webhook", ...HaWebhookTrigger.defaultConfig }],
|
||||
},
|
||||
|
||||
{
|
||||
name: "Zone",
|
||||
triggers: [{ platform: "zone", ...HaZoneTrigger.defaultConfig }],
|
||||
},
|
||||
|
||||
{
|
||||
name: "Tag",
|
||||
triggers: [{ platform: "tag", ...HaTagTrigger.defaultConfig }],
|
||||
},
|
||||
|
||||
{
|
||||
name: "Time",
|
||||
triggers: [{ platform: "time", ...HaTimeTrigger.defaultConfig }],
|
||||
},
|
||||
|
||||
{
|
||||
name: "Template",
|
||||
triggers: [{ platform: "template", ...HaTemplateTrigger.defaultConfig }],
|
||||
},
|
||||
|
||||
{
|
||||
name: "Event",
|
||||
triggers: [{ platform: "event", ...HaEventTrigger.defaultConfig }],
|
||||
},
|
||||
|
||||
{
|
||||
name: "Device Trigger",
|
||||
triggers: [{ platform: "device", ...HaDeviceTrigger.defaultConfig }],
|
||||
},
|
||||
];
|
||||
|
||||
@customElement("demo-automation-editor-trigger")
|
||||
class DemoHaAutomationEditorTrigger extends LitElement {
|
||||
@state() private hass!: HomeAssistant;
|
||||
|
||||
private data: any = SCHEMAS.map((info) => info.triggers);
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
const hass = provideHass(this);
|
||||
hass.updateTranslations(null, "en");
|
||||
hass.updateTranslations("config", "en");
|
||||
mockEntityRegistry(hass);
|
||||
mockDeviceRegistry(hass);
|
||||
mockAreaRegistry(hass);
|
||||
mockHassioSupervisor(hass);
|
||||
}
|
||||
|
||||
protected render(): TemplateResult {
|
||||
const valueChanged = (ev) => {
|
||||
const sampleIdx = ev.target.sampleIdx;
|
||||
this.data[sampleIdx] = ev.detail.value;
|
||||
this.requestUpdate();
|
||||
};
|
||||
return html`
|
||||
${SCHEMAS.map(
|
||||
(info, sampleIdx) => html`
|
||||
<demo-black-white-row
|
||||
.title=${info.name}
|
||||
.value=${this.data[sampleIdx]}
|
||||
>
|
||||
${["light", "dark"].map(
|
||||
(slot) =>
|
||||
html`
|
||||
<ha-automation-trigger
|
||||
slot=${slot}
|
||||
.hass=${this.hass}
|
||||
.triggers=${this.data[sampleIdx]}
|
||||
.sampleIdx=${sampleIdx}
|
||||
@value-changed=${valueChanged}
|
||||
></ha-automation-trigger>
|
||||
`
|
||||
)}
|
||||
</demo-black-white-row>
|
||||
`
|
||||
)}
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"demo-ha-automation-editor-trigger": DemoHaAutomationEditorTrigger;
|
||||
}
|
||||
}
|
||||
@@ -1,177 +1,55 @@
|
||||
import "../../../src/components/ha-switch";
|
||||
import "../../../src/components/ha-formfield";
|
||||
import { mdiClose, mdiHomeAssistant } from "@mdi/js";
|
||||
import { mdiHomeAssistant } from "@mdi/js";
|
||||
import { css, html, LitElement, TemplateResult } from "lit";
|
||||
import { customElement, state } from "lit/decorators";
|
||||
import { customElement } from "lit/decorators";
|
||||
import "../../../src/components/ha-card";
|
||||
import "../../../src/components/ha-chip";
|
||||
import "../../../src/components/ha-chip-set";
|
||||
import type { HaChipSetItem } from "../../../src/components/ha-chip-set";
|
||||
import "../../../src/components/ha-svg-icon";
|
||||
|
||||
const chips: HaChipSetItem[] = [
|
||||
{
|
||||
leadingIcon: mdiHomeAssistant,
|
||||
},
|
||||
{
|
||||
label: "Demo chip",
|
||||
},
|
||||
{
|
||||
leadingIcon: mdiHomeAssistant,
|
||||
label: "Demo chip",
|
||||
},
|
||||
{
|
||||
leadingIcon: mdiHomeAssistant,
|
||||
label: "Demo chip",
|
||||
active: true,
|
||||
},
|
||||
const chips: {
|
||||
icon?: string;
|
||||
content?: string;
|
||||
}[] = [
|
||||
{},
|
||||
{
|
||||
trailingIcon: mdiClose,
|
||||
label: "Demo chip",
|
||||
icon: mdiHomeAssistant,
|
||||
},
|
||||
{
|
||||
label: "Automation",
|
||||
content: "Content",
|
||||
},
|
||||
{
|
||||
label: "Blueprint",
|
||||
},
|
||||
{
|
||||
label: "Script",
|
||||
},
|
||||
{
|
||||
label: "Scene",
|
||||
},
|
||||
{
|
||||
label: "Person",
|
||||
icon: mdiHomeAssistant,
|
||||
content: "Content",
|
||||
},
|
||||
];
|
||||
|
||||
@customElement("demo-ha-chip")
|
||||
export class DemoHaChip extends LitElement {
|
||||
@state() standaloneIsDisabled = false;
|
||||
|
||||
@state() standaloneIsOutlined = false;
|
||||
|
||||
@state() standaloneIsActive = false;
|
||||
|
||||
protected render(): TemplateResult {
|
||||
return html`
|
||||
<div class="filters">
|
||||
<ha-formfield label="Disable standalone chips">
|
||||
<ha-switch @change=${this._toggleDisable}></ha-switch>
|
||||
</ha-formfield>
|
||||
<ha-formfield label="Outline standalone chips">
|
||||
<ha-switch @change=${this._toggleOutline}></ha-switch>
|
||||
</ha-formfield>
|
||||
<ha-formfield label="Activate standalone chips">
|
||||
<ha-switch @change=${this._toggleActive}></ha-switch>
|
||||
</ha-formfield>
|
||||
</div>
|
||||
<ha-card header="Standalone ha-chip demo">
|
||||
<ha-card header="ha-chip demo">
|
||||
<div class="card-content">
|
||||
<div class="standalone">
|
||||
<span>Simple:</span>
|
||||
<ha-chip
|
||||
.active=${this.standaloneIsActive}
|
||||
.outlined=${this.standaloneIsOutlined}
|
||||
?disabled=${this.standaloneIsDisabled}
|
||||
>Demo chip</ha-chip
|
||||
>
|
||||
</div>
|
||||
|
||||
<div class="standalone">
|
||||
<span>Label property:</span>
|
||||
<ha-chip
|
||||
.active=${this.standaloneIsActive}
|
||||
.outlined=${this.standaloneIsOutlined}
|
||||
?disabled=${this.standaloneIsDisabled}
|
||||
label="Demo chip"
|
||||
></ha-chip>
|
||||
</div>
|
||||
|
||||
<div class="standalone">
|
||||
<span>With leadingIcon:</span>
|
||||
<ha-chip
|
||||
.active=${this.standaloneIsActive}
|
||||
.leadingIcon=${mdiHomeAssistant}
|
||||
.outlined=${this.standaloneIsOutlined}
|
||||
?disabled=${this.standaloneIsDisabled}
|
||||
>Demo chip</ha-chip
|
||||
>
|
||||
</div>
|
||||
|
||||
<div class="standalone">
|
||||
<span>With trailingIcon property:</span>
|
||||
<ha-chip
|
||||
.active=${this.standaloneIsActive}
|
||||
.trailingIcon=${mdiHomeAssistant}
|
||||
.outlined=${this.standaloneIsOutlined}
|
||||
?disabled=${this.standaloneIsDisabled}
|
||||
>Demo chip</ha-chip
|
||||
>
|
||||
</div>
|
||||
|
||||
<div class="standalone">
|
||||
<span>With trailing-icon slot:</span>
|
||||
<ha-chip
|
||||
.active=${this.standaloneIsActive}
|
||||
.outlined=${this.standaloneIsOutlined}
|
||||
?disabled=${this.standaloneIsDisabled}
|
||||
>
|
||||
Demo chip
|
||||
<ha-svg-icon
|
||||
slot="trailing-icon"
|
||||
class="trailing"
|
||||
.path=${mdiHomeAssistant}
|
||||
></ha-svg-icon>
|
||||
</ha-chip>
|
||||
</div>
|
||||
</div>
|
||||
</ha-card>
|
||||
|
||||
<ha-card header="ha-chip-set demo">
|
||||
<div class="card-content">
|
||||
<ha-chip-set .items=${chips}> </ha-chip-set>
|
||||
<ha-chip-set
|
||||
.items=${chips.map((chip) => ({ ...chip, outlined: true }))}
|
||||
>
|
||||
</ha-chip-set>
|
||||
${chips.map(
|
||||
(chip) => html`
|
||||
<ha-chip .hasIcon=${chip.icon !== undefined}>
|
||||
${chip.icon
|
||||
? html`<ha-svg-icon slot="icon" .path=${chip.icon}>
|
||||
</ha-svg-icon>`
|
||||
: ""}
|
||||
${chip.content}
|
||||
</ha-chip>
|
||||
`
|
||||
)}
|
||||
</div>
|
||||
</ha-card>
|
||||
`;
|
||||
}
|
||||
|
||||
private _toggleActive() {
|
||||
this.standaloneIsActive = !this.standaloneIsActive;
|
||||
}
|
||||
|
||||
private _toggleDisable() {
|
||||
this.standaloneIsDisabled = !this.standaloneIsDisabled;
|
||||
}
|
||||
|
||||
private _toggleOutline() {
|
||||
this.standaloneIsOutlined = !this.standaloneIsOutlined;
|
||||
}
|
||||
|
||||
static get styles() {
|
||||
return css`
|
||||
ha-card {
|
||||
max-width: 600px;
|
||||
margin: 24px auto;
|
||||
}
|
||||
.standalone {
|
||||
margin: 4px 0;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
.filters {
|
||||
margin: 16px;
|
||||
}
|
||||
ha-formfield {
|
||||
margin: 12px 0;
|
||||
display: block;
|
||||
}
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -181,24 +181,6 @@ class HaGallery extends PolymerElement {
|
||||
this.$.notifications.showDialog({ message: "Alert action clicked" })
|
||||
);
|
||||
|
||||
this.addEventListener("chip-clicked", (ev) =>
|
||||
this.$.notifications.showDialog({
|
||||
message: `Chip ${ev.detail.index} clicked `,
|
||||
})
|
||||
);
|
||||
|
||||
this.addEventListener("chip-clicked", () =>
|
||||
this.$.notifications.showDialog({
|
||||
message: "Chip clicked",
|
||||
})
|
||||
);
|
||||
|
||||
this.addEventListener("chip-clicked-trailing", () =>
|
||||
this.$.notifications.showDialog({
|
||||
message: "Chip trailing icon clicked",
|
||||
})
|
||||
);
|
||||
|
||||
this.addEventListener("hass-more-info", (ev) => {
|
||||
if (ev.detail.entityId) {
|
||||
this.$.notifications.showDialog({
|
||||
|
||||
@@ -11,12 +11,6 @@ import {
|
||||
mdiHomeAssistant,
|
||||
mdiKey,
|
||||
mdiNetwork,
|
||||
mdiNumeric1,
|
||||
mdiNumeric2,
|
||||
mdiNumeric3,
|
||||
mdiNumeric4,
|
||||
mdiNumeric5,
|
||||
mdiNumeric6,
|
||||
mdiPound,
|
||||
mdiShield,
|
||||
} from "@mdi/js";
|
||||
@@ -31,7 +25,7 @@ import "../../../../src/components/buttons/ha-call-api-button";
|
||||
import "../../../../src/components/buttons/ha-progress-button";
|
||||
import "../../../../src/components/ha-alert";
|
||||
import "../../../../src/components/ha-card";
|
||||
import "../../../../src/components/ha-chip-set";
|
||||
import "../../../../src/components/ha-label-badge";
|
||||
import "../../../../src/components/ha-markdown";
|
||||
import "../../../../src/components/ha-settings-row";
|
||||
import "../../../../src/components/ha-svg-icon";
|
||||
@@ -79,15 +73,6 @@ const STAGE_ICON = {
|
||||
deprecated: mdiExclamationThick,
|
||||
};
|
||||
|
||||
const RATING_ICON = {
|
||||
1: mdiNumeric1,
|
||||
2: mdiNumeric2,
|
||||
3: mdiNumeric3,
|
||||
4: mdiNumeric4,
|
||||
5: mdiNumeric5,
|
||||
6: mdiNumeric6,
|
||||
};
|
||||
|
||||
@customElement("hassio-addon-info")
|
||||
class HassioAddonInfo extends LitElement {
|
||||
@property({ type: Boolean }) public narrow!: boolean;
|
||||
@@ -125,137 +110,6 @@ class HassioAddonInfo extends LitElement {
|
||||
)}`,
|
||||
},
|
||||
];
|
||||
const capabilities = [
|
||||
{
|
||||
id: "rating",
|
||||
icon: RATING_ICON[this.addon.rating],
|
||||
class: [6, 5].includes(Number(this.addon.rating))
|
||||
? "green"
|
||||
: [3, 4].includes(Number(this.addon.rating))
|
||||
? "yellow"
|
||||
: "red",
|
||||
label: this.supervisor.localize(
|
||||
"addon.dashboard.capability.label.rating"
|
||||
),
|
||||
},
|
||||
];
|
||||
|
||||
if (this.addon.stage !== "stable") {
|
||||
capabilities.push({
|
||||
id: "rating",
|
||||
icon: STAGE_ICON[this.addon.stage],
|
||||
class:
|
||||
this.addon.stage === "experimental"
|
||||
? "yellow"
|
||||
: this.addon.stage === "deprecated"
|
||||
? "red"
|
||||
: "",
|
||||
label: this.supervisor.localize(
|
||||
`addon.dashboard.capability.stages.${this.addon.stage}`
|
||||
),
|
||||
});
|
||||
}
|
||||
|
||||
if (this.addon.host_network) {
|
||||
capabilities.push({
|
||||
id: "host_network",
|
||||
icon: mdiNetwork,
|
||||
class: "",
|
||||
label: this.supervisor.localize(
|
||||
"addon.dashboard.capability.label.host"
|
||||
),
|
||||
});
|
||||
}
|
||||
|
||||
if (this.addon.full_access) {
|
||||
capabilities.push({
|
||||
id: "full_access",
|
||||
icon: mdiChip,
|
||||
class: "",
|
||||
label: this.supervisor.localize(
|
||||
"addon.dashboard.capability.label.hardware"
|
||||
),
|
||||
});
|
||||
}
|
||||
|
||||
if (this.addon.homeassistant_api) {
|
||||
capabilities.push({
|
||||
id: "homeassistant_api",
|
||||
icon: mdiHomeAssistant,
|
||||
class: "",
|
||||
label: this.supervisor.localize(
|
||||
"addon.dashboard.capability.label.core"
|
||||
),
|
||||
});
|
||||
}
|
||||
|
||||
if (this._computeHassioApi) {
|
||||
capabilities.push({
|
||||
id: "hassio_api",
|
||||
icon: mdiHomeAssistant,
|
||||
class: "",
|
||||
label:
|
||||
this.supervisor.localize(
|
||||
`addon.dashboard.capability.role.${this.addon.hassio_role}`
|
||||
) || this.addon.hassio_role,
|
||||
});
|
||||
}
|
||||
|
||||
if (this.addon.docker_api) {
|
||||
capabilities.push({
|
||||
id: "docker_api",
|
||||
icon: mdiDocker,
|
||||
class: "",
|
||||
label: this.supervisor.localize(
|
||||
"addon.dashboard.capability.label.docker"
|
||||
),
|
||||
});
|
||||
}
|
||||
|
||||
if (this.addon.host_pid) {
|
||||
capabilities.push({
|
||||
id: "host_pid",
|
||||
icon: mdiPound,
|
||||
class: "",
|
||||
label: this.supervisor.localize(
|
||||
"addon.dashboard.capability.label.host_pid"
|
||||
),
|
||||
});
|
||||
}
|
||||
|
||||
if (this.addon.apparmor !== "default") {
|
||||
capabilities.push({
|
||||
id: "apparmor",
|
||||
icon: mdiShield,
|
||||
class: this._computeApparmorClassName,
|
||||
label: this.supervisor.localize(
|
||||
"addon.dashboard.capability.label.apparmor"
|
||||
),
|
||||
});
|
||||
}
|
||||
|
||||
if (this.addon.auth_api) {
|
||||
capabilities.push({
|
||||
id: "auth_api",
|
||||
icon: mdiKey,
|
||||
class: "",
|
||||
label: this.supervisor.localize(
|
||||
"addon.dashboard.capability.label.auth"
|
||||
),
|
||||
});
|
||||
}
|
||||
|
||||
if (this.addon.ingress) {
|
||||
capabilities.push({
|
||||
id: "ingress",
|
||||
icon: mdiCursorDefaultClickOutline,
|
||||
class: "",
|
||||
label: this.supervisor.localize(
|
||||
"addon.dashboard.capability.label.ingress"
|
||||
),
|
||||
});
|
||||
}
|
||||
|
||||
return html`
|
||||
${this.addon.update_available
|
||||
? html`
|
||||
@@ -392,21 +246,6 @@ class HassioAddonInfo extends LitElement {
|
||||
>`}
|
||||
</div>
|
||||
|
||||
<div class="capabilities">
|
||||
<ha-chip-set>
|
||||
${capabilities.map(
|
||||
(capability) => html`<ha-chip
|
||||
@click=${this._showMoreInfo}
|
||||
.leadingIcon=${capability.icon}
|
||||
.label=${capability.label}
|
||||
.id=${capability.id}
|
||||
class=${capability.class}
|
||||
outlined
|
||||
></ha-chip>`
|
||||
)}
|
||||
</ha-chip-set>
|
||||
</div>
|
||||
|
||||
<div class="description light-color">
|
||||
${this.addon.description}.<br />
|
||||
${this.supervisor.localize(
|
||||
@@ -427,6 +266,172 @@ class HassioAddonInfo extends LitElement {
|
||||
/>
|
||||
`
|
||||
: ""}
|
||||
<div class="security">
|
||||
${this.addon.stage !== "stable"
|
||||
? html` <ha-label-badge
|
||||
class=${classMap({
|
||||
yellow: this.addon.stage === "experimental",
|
||||
red: this.addon.stage === "deprecated",
|
||||
})}
|
||||
@click=${this._showMoreInfo}
|
||||
id="stage"
|
||||
.label=${this.supervisor.localize(
|
||||
"addon.dashboard.capability.label.stage"
|
||||
)}
|
||||
description=""
|
||||
>
|
||||
<ha-svg-icon
|
||||
.path=${STAGE_ICON[this.addon.stage]}
|
||||
></ha-svg-icon>
|
||||
</ha-label-badge>`
|
||||
: ""}
|
||||
|
||||
<ha-label-badge
|
||||
class=${classMap({
|
||||
green: [5, 6].includes(Number(this.addon.rating)),
|
||||
yellow: [3, 4].includes(Number(this.addon.rating)),
|
||||
red: [1, 2].includes(Number(this.addon.rating)),
|
||||
})}
|
||||
@click=${this._showMoreInfo}
|
||||
id="rating"
|
||||
label="rating"
|
||||
description=""
|
||||
>
|
||||
${this.addon.rating}
|
||||
</ha-label-badge>
|
||||
${this.addon.host_network
|
||||
? html`
|
||||
<ha-label-badge
|
||||
@click=${this._showMoreInfo}
|
||||
id="host_network"
|
||||
.label=${this.supervisor.localize(
|
||||
"addon.dashboard.capability.label.host"
|
||||
)}
|
||||
description=""
|
||||
>
|
||||
<ha-svg-icon .path=${mdiNetwork}></ha-svg-icon>
|
||||
</ha-label-badge>
|
||||
`
|
||||
: ""}
|
||||
${this.addon.full_access
|
||||
? html`
|
||||
<ha-label-badge
|
||||
@click=${this._showMoreInfo}
|
||||
id="full_access"
|
||||
.label=${this.supervisor.localize(
|
||||
"addon.dashboard.capability.label.hardware"
|
||||
)}
|
||||
description=""
|
||||
>
|
||||
<ha-svg-icon .path=${mdiChip}></ha-svg-icon>
|
||||
</ha-label-badge>
|
||||
`
|
||||
: ""}
|
||||
${this.addon.homeassistant_api
|
||||
? html`
|
||||
<ha-label-badge
|
||||
@click=${this._showMoreInfo}
|
||||
id="homeassistant_api"
|
||||
.label=${this.supervisor.localize(
|
||||
"addon.dashboard.capability.label.hass"
|
||||
)}
|
||||
description=""
|
||||
>
|
||||
<ha-svg-icon .path=${mdiHomeAssistant}></ha-svg-icon>
|
||||
</ha-label-badge>
|
||||
`
|
||||
: ""}
|
||||
${this._computeHassioApi
|
||||
? html`
|
||||
<ha-label-badge
|
||||
@click=${this._showMoreInfo}
|
||||
id="hassio_api"
|
||||
.label=${this.supervisor.localize(
|
||||
"addon.dashboard.capability.label.hassio"
|
||||
)}
|
||||
.description=${this.supervisor.localize(
|
||||
`addon.dashboard.capability.role.${this.addon.hassio_role}`
|
||||
) || this.addon.hassio_role}
|
||||
>
|
||||
<ha-svg-icon .path=${mdiHomeAssistant}></ha-svg-icon>
|
||||
</ha-label-badge>
|
||||
`
|
||||
: ""}
|
||||
${this.addon.docker_api
|
||||
? html`
|
||||
<ha-label-badge
|
||||
@click=${this._showMoreInfo}
|
||||
id="docker_api"
|
||||
.label=${this.supervisor.localize(
|
||||
"addon.dashboard.capability.label.docker"
|
||||
)}
|
||||
description=""
|
||||
>
|
||||
<ha-svg-icon .path=${mdiDocker}></ha-svg-icon>
|
||||
</ha-label-badge>
|
||||
`
|
||||
: ""}
|
||||
${this.addon.host_pid
|
||||
? html`
|
||||
<ha-label-badge
|
||||
@click=${this._showMoreInfo}
|
||||
id="host_pid"
|
||||
.label=${this.supervisor.localize(
|
||||
"addon.dashboard.capability.label.host_pid"
|
||||
)}
|
||||
description=""
|
||||
>
|
||||
<ha-svg-icon .path=${mdiPound}></ha-svg-icon>
|
||||
</ha-label-badge>
|
||||
`
|
||||
: ""}
|
||||
${this.addon.apparmor
|
||||
? html`
|
||||
<ha-label-badge
|
||||
@click=${this._showMoreInfo}
|
||||
class=${this._computeApparmorClassName}
|
||||
id="apparmor"
|
||||
.label=${this.supervisor.localize(
|
||||
"addon.dashboard.capability.label.apparmor"
|
||||
)}
|
||||
description=""
|
||||
>
|
||||
<ha-svg-icon .path=${mdiShield}></ha-svg-icon>
|
||||
</ha-label-badge>
|
||||
`
|
||||
: ""}
|
||||
${this.addon.auth_api
|
||||
? html`
|
||||
<ha-label-badge
|
||||
@click=${this._showMoreInfo}
|
||||
id="auth_api"
|
||||
.label=${this.supervisor.localize(
|
||||
"addon.dashboard.capability.label.auth"
|
||||
)}
|
||||
description=""
|
||||
>
|
||||
<ha-svg-icon .path=${mdiKey}></ha-svg-icon>
|
||||
</ha-label-badge>
|
||||
`
|
||||
: ""}
|
||||
${this.addon.ingress
|
||||
? html`
|
||||
<ha-label-badge
|
||||
@click=${this._showMoreInfo}
|
||||
id="ingress"
|
||||
.label=${this.supervisor.localize(
|
||||
"addon.dashboard.capability.label.ingress"
|
||||
)}
|
||||
description=""
|
||||
>
|
||||
<ha-svg-icon
|
||||
.path=${mdiCursorDefaultClickOutline}
|
||||
></ha-svg-icon>
|
||||
</ha-label-badge>
|
||||
`
|
||||
: ""}
|
||||
</div>
|
||||
|
||||
${this.addon.version
|
||||
? html`
|
||||
<div
|
||||
@@ -1170,29 +1175,34 @@ class HassioAddonInfo extends LitElement {
|
||||
.description a {
|
||||
color: var(--primary-color);
|
||||
}
|
||||
ha-chip {
|
||||
text-transform: capitalize;
|
||||
}
|
||||
|
||||
.red {
|
||||
--ha-chip-background-color: var(--label-badge-red, #df4c1e);
|
||||
--ha-label-badge-color: var(--label-badge-red, #df4c1e);
|
||||
}
|
||||
.blue {
|
||||
--ha-chip-background-color: var(--label-badge-blue, #039be5);
|
||||
--ha-label-badge-color: var(--label-badge-blue, #039be5);
|
||||
}
|
||||
.green {
|
||||
--ha-chip-background-color: var(--label-badge-green, #0da035);
|
||||
--ha-label-badge-color: var(--label-badge-green, #0da035);
|
||||
}
|
||||
.yellow {
|
||||
--ha-chip-background-color: var(--label-badge-yellow, #f4b400);
|
||||
--ha-label-badge-color: var(--label-badge-yellow, #f4b400);
|
||||
}
|
||||
.capabilities {
|
||||
margin: -16px 0 8px;
|
||||
.security {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
.card-actions {
|
||||
justify-content: space-between;
|
||||
display: flex;
|
||||
}
|
||||
.security h3 {
|
||||
margin-bottom: 8px;
|
||||
font-weight: normal;
|
||||
}
|
||||
.security ha-label-badge {
|
||||
cursor: pointer;
|
||||
margin-right: 4px;
|
||||
--ha-label-badge-padding: 8px 0 0 0;
|
||||
}
|
||||
.changelog {
|
||||
display: contents;
|
||||
}
|
||||
@@ -1232,9 +1242,6 @@ class HassioAddonInfo extends LitElement {
|
||||
}
|
||||
|
||||
@media (max-width: 720px) {
|
||||
ha-chip {
|
||||
line-height: 36px;
|
||||
}
|
||||
.addon-options {
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
+2
-2
@@ -67,8 +67,8 @@
|
||||
"@material/mwc-tab-bar": "0.25.2",
|
||||
"@material/mwc-textfield": "0.25.2",
|
||||
"@material/top-app-bar": "14.0.0-canary.353ca7e9f.0",
|
||||
"@mdi/js": "6.4.95",
|
||||
"@mdi/svg": "6.4.95",
|
||||
"@mdi/js": "6.3.95",
|
||||
"@mdi/svg": "6.3.95",
|
||||
"@polymer/app-layout": "^3.1.0",
|
||||
"@polymer/iron-flex-layout": "^3.0.1",
|
||||
"@polymer/iron-icon": "^3.0.1",
|
||||
|
||||
@@ -1,45 +1,43 @@
|
||||
import {
|
||||
mdiAlertCircle,
|
||||
mdiBattery,
|
||||
mdiBatteryCharging,
|
||||
mdiBatteryOutline,
|
||||
mdiBrightness5,
|
||||
mdiBrightness7,
|
||||
mdiCheckboxMarkedCircle,
|
||||
mdiCheckCircle,
|
||||
mdiCropPortrait,
|
||||
mdiBatteryCharging,
|
||||
mdiThermometer,
|
||||
mdiSnowflake,
|
||||
mdiServerNetworkOff,
|
||||
mdiServerNetwork,
|
||||
mdiDoorClosed,
|
||||
mdiDoorOpen,
|
||||
mdiFire,
|
||||
mdiGarage,
|
||||
mdiGarageOpen,
|
||||
mdiHome,
|
||||
mdiHomeOutline,
|
||||
mdiPowerPlugOff,
|
||||
mdiPowerPlug,
|
||||
mdiCheckCircle,
|
||||
mdiAlertCircle,
|
||||
mdiSmoke,
|
||||
mdiFire,
|
||||
mdiBrightness5,
|
||||
mdiBrightness7,
|
||||
mdiLock,
|
||||
mdiLockOpen,
|
||||
mdiMusicNote,
|
||||
mdiMusicNoteOff,
|
||||
mdiPackage,
|
||||
mdiPackageUp,
|
||||
mdiPlay,
|
||||
mdiPowerPlug,
|
||||
mdiPowerPlugOff,
|
||||
mdiRadioboxBlank,
|
||||
mdiWaterOff,
|
||||
mdiWater,
|
||||
mdiWalk,
|
||||
mdiRun,
|
||||
mdiServerNetwork,
|
||||
mdiServerNetworkOff,
|
||||
mdiSmoke,
|
||||
mdiSnowflake,
|
||||
mdiHomeOutline,
|
||||
mdiHome,
|
||||
mdiSquare,
|
||||
mdiSquareOutline,
|
||||
mdiStop,
|
||||
mdiThermometer,
|
||||
mdiMusicNoteOff,
|
||||
mdiMusicNote,
|
||||
mdiPackage,
|
||||
mdiPackageUp,
|
||||
mdiCropPortrait,
|
||||
mdiVibrate,
|
||||
mdiWalk,
|
||||
mdiWater,
|
||||
mdiWaterOff,
|
||||
mdiWindowClosed,
|
||||
mdiWindowOpen,
|
||||
mdiRadioboxBlank,
|
||||
mdiCheckboxMarkedCircle,
|
||||
} from "@mdi/js";
|
||||
import { HassEntity } from "home-assistant-js-websocket";
|
||||
|
||||
@@ -87,8 +85,6 @@ export const binarySensorIcon = (state?: string, stateObj?: HassEntity) => {
|
||||
return is_off ? mdiPowerPlugOff : mdiPowerPlug;
|
||||
case "presence":
|
||||
return is_off ? mdiHomeOutline : mdiHome;
|
||||
case "running":
|
||||
return is_off ? mdiStop : mdiPlay;
|
||||
case "sound":
|
||||
return is_off ? mdiMusicNoteOff : mdiMusicNote;
|
||||
case "update":
|
||||
|
||||
@@ -1,24 +0,0 @@
|
||||
/**
|
||||
* Strips a device name from an entity name.
|
||||
* @param entityName the entity name
|
||||
* @param lowerCasedPrefixWithSpaceSuffix the prefix to strip, lower cased with a space suffix
|
||||
* @returns
|
||||
*/
|
||||
export const stripPrefixFromEntityName = (
|
||||
entityName: string,
|
||||
lowerCasedPrefixWithSpaceSuffix: string
|
||||
) => {
|
||||
if (!entityName.toLowerCase().startsWith(lowerCasedPrefixWithSpaceSuffix)) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const newName = entityName.substring(lowerCasedPrefixWithSpaceSuffix.length);
|
||||
|
||||
// If first word already has an upper case letter (e.g. from brand name)
|
||||
// leave as-is, otherwise capitalize the first word.
|
||||
return hasUpperCase(newName.substr(0, newName.indexOf(" ")))
|
||||
? newName
|
||||
: newName[0].toUpperCase() + newName.slice(1);
|
||||
};
|
||||
|
||||
const hasUpperCase = (str: string): boolean => str.toLowerCase() !== str;
|
||||
@@ -12,8 +12,8 @@ export const slugify = (value: string, delimiter = "_") => {
|
||||
.replace(p, (c) => b.charAt(a.indexOf(c))) // Replace special characters
|
||||
.replace(/&/g, `${delimiter}and${delimiter}`) // Replace & with 'and'
|
||||
.replace(/[^\w-]+/g, "") // Remove all non-word characters
|
||||
.replace(/-/g, delimiter) // Replace - with delimiter
|
||||
.replace(new RegExp(`(${delimiter})\\1+`, "g"), "$1") // Replace multiple delimiters with single delimiter
|
||||
.replace(new RegExp(`^${delimiter}+`), "") // Trim delimiter from start of text
|
||||
.replace(new RegExp(`${delimiter}+$`), ""); // Trim delimiter from end of text
|
||||
.replace(/-/, delimiter) // Replace - with delimiter
|
||||
.replace(new RegExp(`/${delimiter}${delimiter}+/`, "g"), delimiter) // Replace multiple delimiters with single delimiter
|
||||
.replace(new RegExp(`/^${delimiter}+/`), "") // Trim delimiter from start of text
|
||||
.replace(new RegExp(`/-+$/`), ""); // Trim delimiter from end of text
|
||||
};
|
||||
|
||||
@@ -9,47 +9,49 @@ import {
|
||||
unsafeCSS,
|
||||
} from "lit";
|
||||
import { customElement, property } from "lit/decorators";
|
||||
import { fireEvent } from "../common/dom/fire_event";
|
||||
import "./ha-chip";
|
||||
|
||||
export interface HaChipSetItem {
|
||||
label?: string;
|
||||
leadingIcon?: string;
|
||||
trailingIcon?: string;
|
||||
outlined?: boolean;
|
||||
active?: boolean;
|
||||
declare global {
|
||||
// for fire event
|
||||
interface HASSDomEvents {
|
||||
"chip-clicked": { index: string };
|
||||
}
|
||||
}
|
||||
|
||||
@customElement("ha-chip-set")
|
||||
export class HaChipSet extends LitElement {
|
||||
@property({ attribute: false }) public items?: HaChipSetItem[];
|
||||
@property() public items = [];
|
||||
|
||||
protected render(): TemplateResult {
|
||||
if (this.items.length === 0) {
|
||||
return html``;
|
||||
}
|
||||
return html`
|
||||
<div class="mdc-chip-set">
|
||||
${this.items
|
||||
? this.items.map(
|
||||
(item, idx) =>
|
||||
html`
|
||||
<ha-chip
|
||||
.index=${idx}
|
||||
.active=${item.active || false}
|
||||
.label=${item.label}
|
||||
.leadingIcon=${item.leadingIcon}
|
||||
.trailingIcon=${item.trailingIcon}
|
||||
?outlined=${item.outlined}
|
||||
>
|
||||
</ha-chip>
|
||||
`
|
||||
)
|
||||
: html`<slot></slot>`}
|
||||
${this.items.map(
|
||||
(item, idx) =>
|
||||
html`
|
||||
<ha-chip .index=${idx} @click=${this._handleClick}>
|
||||
${item}
|
||||
</ha-chip>
|
||||
`
|
||||
)}
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
private _handleClick(ev): void {
|
||||
fireEvent(this, "chip-clicked", {
|
||||
index: ev.currentTarget.index,
|
||||
});
|
||||
}
|
||||
|
||||
static get styles(): CSSResultGroup {
|
||||
return css`
|
||||
${unsafeCSS(chipStyles)}
|
||||
.mdc-chip-set > ha-chip, slot::slotted(ha-chip) {
|
||||
|
||||
ha-chip {
|
||||
margin: 4px;
|
||||
}
|
||||
`;
|
||||
|
||||
+19
-137
@@ -1,5 +1,4 @@
|
||||
import "./ha-circular-progress";
|
||||
import "./ha-svg-icon"; // @ts-ignore
|
||||
// @ts-ignore
|
||||
import chipStyles from "@material/chips/dist/mdc.chips.min.css";
|
||||
import {
|
||||
css,
|
||||
@@ -10,173 +9,56 @@ import {
|
||||
unsafeCSS,
|
||||
} from "lit";
|
||||
import { customElement, property } from "lit/decorators";
|
||||
import { fireEvent } from "../common/dom/fire_event";
|
||||
|
||||
declare global {
|
||||
// for fire event
|
||||
interface HASSDomEvents {
|
||||
"chip-clicked": { index: number | undefined };
|
||||
"chip-clicked-trailing": { index: number | undefined };
|
||||
"chip-clicked": { index: string };
|
||||
}
|
||||
}
|
||||
|
||||
@customElement("ha-chip")
|
||||
export class HaChip extends LitElement {
|
||||
@property({ type: Number }) public index?: number;
|
||||
@property() public index = 0;
|
||||
|
||||
@property({ type: Boolean }) public outlined = false;
|
||||
|
||||
@property({ type: Boolean }) public active = false;
|
||||
|
||||
@property() public label?: string;
|
||||
|
||||
@property() public leadingIcon?: string;
|
||||
|
||||
@property() public trailingIcon?: string;
|
||||
@property({ type: Boolean }) public hasIcon = false;
|
||||
|
||||
protected render(): TemplateResult {
|
||||
return html`
|
||||
<div
|
||||
class="mdc-chip ${this.outlined ? "outlined" : ""}"
|
||||
@click=${this._handleClick}
|
||||
>
|
||||
${this.leadingIcon
|
||||
? html`<span role="gridcell">
|
||||
<span role="button" tabindex="0" class="mdc-chip__primary-action">
|
||||
${this.active
|
||||
? html`<ha-circular-progress alt="active" size="tiny" active>
|
||||
</ha-circular-progress>`
|
||||
: html`<ha-svg-icon
|
||||
class="mdc-chip__icon mdc-chip__icon--leading"
|
||||
.path=${this.leadingIcon}
|
||||
>
|
||||
</ha-svg-icon>`}
|
||||
</span>
|
||||
</span>`
|
||||
: ""}
|
||||
<div class="mdc-chip" .index=${this.index}>
|
||||
${this.hasIcon
|
||||
? html`<div class="mdc-chip__icon mdc-chip__icon--leading">
|
||||
<slot name="icon"></slot>
|
||||
</div>`
|
||||
: null}
|
||||
<div class="mdc-chip__ripple"></div>
|
||||
<span role="gridcell">
|
||||
<span role="row" tabindex="0" class="mdc-chip__primary-action">
|
||||
<span class="mdc-chip__text">
|
||||
${this.label || html`<slot></slot>`}
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
<span role="gridcell">
|
||||
<span role="button" tabindex="-1" class="mdc-chip__primary-action">
|
||||
${this.trailingIcon
|
||||
? html`<ha-svg-icon
|
||||
@click=${this._handleTrailingClick}
|
||||
class="mdc-chip__icon mdc-chip__icon--trailing"
|
||||
.path=${this.trailingIcon}
|
||||
>
|
||||
</ha-svg-icon>`
|
||||
: html`<slot name="trailing-icon"></slot>`}
|
||||
<span role="button" tabindex="0" class="mdc-chip__primary-action">
|
||||
<span class="mdc-chip__text"><slot></slot></span>
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
private _handleClick(): void {
|
||||
fireEvent(this, "chip-clicked", { index: this.index });
|
||||
}
|
||||
|
||||
private _handleTrailingClick(): void {
|
||||
fireEvent(this, "chip-clicked-trailing", {
|
||||
index: this.index,
|
||||
});
|
||||
}
|
||||
|
||||
static get styles(): CSSResultGroup {
|
||||
return css`
|
||||
${unsafeCSS(chipStyles)}
|
||||
.mdc-chip {
|
||||
background-color: var(--ha-chip-background-color, var(--primary-color));
|
||||
color: var(--ha-chip-text-color, var(--text-primary-color));
|
||||
background-color: var(
|
||||
--ha-chip-background-color,
|
||||
rgba(var(--rgb-primary-text-color), 0.15)
|
||||
);
|
||||
color: var(--ha-chip-text-color, var(--primary-text-color));
|
||||
}
|
||||
|
||||
.mdc-chip:hover {
|
||||
color: var(--ha-chip-text-color, var(--text-primary-color));
|
||||
}
|
||||
|
||||
.mdc-chip.outlined {
|
||||
margin: 0 -1px !important;
|
||||
border: 1px solid var(--ha-chip-background-color, var(--primary-color));
|
||||
background-color: var(
|
||||
--ha-chip-outlined-background-color,
|
||||
var(--card-background-color)
|
||||
);
|
||||
color: var(--ha-chip-outlined-text-color, var(--primary-text-color));
|
||||
}
|
||||
|
||||
.mdc-chip:not(.outlined) .mdc-chip__icon.mdc-chip__icon--leading {
|
||||
margin-left: -12px !important;
|
||||
margin-right: -2px;
|
||||
color: var(--ha-chip-icon-color, var(--text-primary-color));
|
||||
}
|
||||
|
||||
ha-circular-progress {
|
||||
--mdc-theme-primary: var(
|
||||
--ha-chip-icon-color,
|
||||
var(--text-primary-color)
|
||||
);
|
||||
padding: 8px;
|
||||
margin-left: -12px !important;
|
||||
margin-right: -2px;
|
||||
}
|
||||
|
||||
.mdc-chip.outlined ha-circular-progress {
|
||||
border-radius: 50%;
|
||||
margin-right: 4px;
|
||||
background-color: var(--ha-chip-background-color, var(--primary-color));
|
||||
--mdc-theme-primary: var(
|
||||
--ha-chip-icon-color,
|
||||
var(--text-primary-color)
|
||||
);
|
||||
}
|
||||
|
||||
.mdc-chip.outlined ha-svg-icon,
|
||||
slot[name="trailing-icon"]::slotted(ha-svg-icon) {
|
||||
border-radius: 50%;
|
||||
background-color: var(--ha-chip-background-color, var(--primary-color));
|
||||
color: var(--ha-chip-icon-color, var(--text-primary-color));
|
||||
}
|
||||
|
||||
.mdc-chip.outlined .mdc-chip__icon.mdc-chip__icon--leading {
|
||||
margin-left: -13px !important;
|
||||
color: var(--ha-chip-icon-color, var(--text-primary-color));
|
||||
}
|
||||
|
||||
.mdc-chip__icon.mdc-chip__icon--trailing,
|
||||
slot[name="trailing-icon"]::slotted(ha-svg-icon) {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
font-size: 18px;
|
||||
padding: 2px;
|
||||
color: var(--ha-chip-icon-color, var(--text-primary-color));
|
||||
margin-right: -8px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
--mdc-icon-size: 12px;
|
||||
}
|
||||
|
||||
slot[name="trailing-icon"]::slotted(ha-svg-icon) {
|
||||
margin-left: 4px;
|
||||
color: var(--ha-chip-text-color, var(--primary-text-color));
|
||||
}
|
||||
|
||||
.mdc-chip__icon--leading {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: inherit;
|
||||
border-radius: 50%;
|
||||
padding: 6px;
|
||||
--mdc-icon-size: 20px;
|
||||
}
|
||||
:host([disabled]) .mdc-chip {
|
||||
opacity: var(--light-disabled-opacity);
|
||||
pointer-events: none;
|
||||
color: var(--ha-chip-icon-color, var(--ha-chip-text-color));
|
||||
}
|
||||
`;
|
||||
}
|
||||
|
||||
@@ -52,7 +52,6 @@ export class HaFormSelect extends LitElement implements HaFormElement {
|
||||
return html`
|
||||
<mwc-select
|
||||
fixedMenuPosition
|
||||
naturalMenuWidth
|
||||
.label=${this.label}
|
||||
.value=${this.data}
|
||||
.disabled=${this.disabled}
|
||||
|
||||
@@ -52,6 +52,18 @@ const mdiDeprecatedIcons: DeprecatedIcon = {
|
||||
newName: "cast-variant",
|
||||
removeIn: "2021.12",
|
||||
},
|
||||
application: {
|
||||
newName: "application-outline",
|
||||
removeIn: "2021.12",
|
||||
},
|
||||
"application-cog": {
|
||||
newName: "application-cog-outline",
|
||||
removeIn: "2021.12",
|
||||
},
|
||||
"application-settings": {
|
||||
newName: "application-settings-outline",
|
||||
removeIn: "2021.12",
|
||||
},
|
||||
bandcamp: {
|
||||
removeIn: "2021.12",
|
||||
},
|
||||
@@ -65,6 +77,14 @@ const mdiDeprecatedIcons: DeprecatedIcon = {
|
||||
newName: "cross-bolnisi",
|
||||
removeIn: "2021.12",
|
||||
},
|
||||
"boom-gate-up": {
|
||||
newName: "boom-gate-arrow-up",
|
||||
removeIn: "2021.12",
|
||||
},
|
||||
"boom-gate-up-outline": {
|
||||
newName: "boom-gate-arrow-up-outline",
|
||||
removeIn: "2021.12",
|
||||
},
|
||||
"boom-gate-down": {
|
||||
newName: "boom-gate-arrow-down",
|
||||
removeIn: "2021.12",
|
||||
|
||||
+190
-103
@@ -1,3 +1,5 @@
|
||||
// @ts-ignore
|
||||
import chipStyles from "@material/chips/dist/mdc.chips.min.css";
|
||||
import "@material/mwc-button/mwc-button";
|
||||
import {
|
||||
mdiClose,
|
||||
@@ -12,13 +14,13 @@ import {
|
||||
HassServiceTarget,
|
||||
UnsubscribeFunc,
|
||||
} from "home-assistant-js-websocket";
|
||||
import { css, CSSResultGroup, html, LitElement } from "lit";
|
||||
import { css, CSSResultGroup, html, LitElement, unsafeCSS } from "lit";
|
||||
import { customElement, property, query, state } from "lit/decorators";
|
||||
import { classMap } from "lit/directives/class-map";
|
||||
import { fireEvent } from "../common/dom/fire_event";
|
||||
import { ensureArray } from "../common/ensure-array";
|
||||
import { computeDomain } from "../common/entity/compute_domain";
|
||||
import { computeStateName } from "../common/entity/compute_state_name";
|
||||
import { stateIconPath } from "../common/entity/state_icon_path";
|
||||
import {
|
||||
AreaRegistryEntry,
|
||||
subscribeAreaRegistry,
|
||||
@@ -39,7 +41,6 @@ import type { HaDevicePickerDeviceFilterFunc } from "./device/ha-device-picker";
|
||||
import "./entity/ha-entity-picker";
|
||||
import type { HaEntityPickerEntityFilterFunc } from "./entity/ha-entity-picker";
|
||||
import "./ha-area-picker";
|
||||
import "./ha-chip-set";
|
||||
import "./ha-icon-button";
|
||||
import "./ha-svg-icon";
|
||||
|
||||
@@ -113,7 +114,7 @@ export class HaTargetPicker extends SubscribeMixin(LitElement) {
|
||||
if (!this._areas || !this._devices || !this._entities) {
|
||||
return html``;
|
||||
}
|
||||
return html`<ha-chip-set>
|
||||
return html`<div class="mdc-chip-set items">
|
||||
${this.value?.area_id
|
||||
? ensureArray(this.value.area_id).map((area_id) => {
|
||||
const area = this._areas![area_id];
|
||||
@@ -149,39 +150,70 @@ export class HaTargetPicker extends SubscribeMixin(LitElement) {
|
||||
);
|
||||
})
|
||||
: ""}
|
||||
</ha-chip-set>
|
||||
</div>
|
||||
${this._renderPicker()}
|
||||
<ha-chip-set>
|
||||
<ha-chip
|
||||
class="area_id add"
|
||||
<div class="mdc-chip-set">
|
||||
<div
|
||||
class="mdc-chip area_id add"
|
||||
.type=${"area_id"}
|
||||
@click=${this._showPicker}
|
||||
.leadingIcon=${mdiPlus}
|
||||
.label=${this.hass.localize(
|
||||
"ui.components.target-picker.add_area_id"
|
||||
)}
|
||||
>
|
||||
</ha-chip>
|
||||
<ha-chip
|
||||
class="device_id add"
|
||||
<div class="mdc-chip__ripple"></div>
|
||||
<ha-svg-icon
|
||||
class="mdc-chip__icon mdc-chip__icon--leading"
|
||||
.path=${mdiPlus}
|
||||
></ha-svg-icon>
|
||||
<span role="gridcell">
|
||||
<span role="button" tabindex="0" class="mdc-chip__primary-action">
|
||||
<span class="mdc-chip__text"
|
||||
>${this.hass.localize(
|
||||
"ui.components.target-picker.add_area_id"
|
||||
)}</span
|
||||
>
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
class="mdc-chip device_id add"
|
||||
.type=${"device_id"}
|
||||
@click=${this._showPicker}
|
||||
.leadingIcon=${mdiPlus}
|
||||
.label=${this.hass.localize(
|
||||
"ui.components.target-picker.add_device_id"
|
||||
)}
|
||||
>
|
||||
</ha-chip>
|
||||
<ha-chip
|
||||
class="entity_id add"
|
||||
<div class="mdc-chip__ripple"></div>
|
||||
<ha-svg-icon
|
||||
class="mdc-chip__icon mdc-chip__icon--leading"
|
||||
.path=${mdiPlus}
|
||||
></ha-svg-icon>
|
||||
<span role="gridcell">
|
||||
<span role="button" tabindex="0" class="mdc-chip__primary-action">
|
||||
<span class="mdc-chip__text"
|
||||
>${this.hass.localize(
|
||||
"ui.components.target-picker.add_device_id"
|
||||
)}</span
|
||||
>
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
class="mdc-chip entity_id add"
|
||||
.type=${"entity_id"}
|
||||
@click=${this._showPicker}
|
||||
.leadingIcon=${mdiPlus}
|
||||
.label=${this.hass.localize(
|
||||
"ui.components.target-picker.add_entity_id"
|
||||
)}
|
||||
></ha-chip>
|
||||
</ha-chip-set>`;
|
||||
>
|
||||
<div class="mdc-chip__ripple"></div>
|
||||
<ha-svg-icon
|
||||
class="mdc-chip__icon mdc-chip__icon--leading"
|
||||
.path=${mdiPlus}
|
||||
></ha-svg-icon>
|
||||
<span role="gridcell">
|
||||
<span role="button" tabindex="0" class="mdc-chip__primary-action">
|
||||
<span class="mdc-chip__text"
|
||||
>${this.hass.localize(
|
||||
"ui.components.target-picker.add_entity_id"
|
||||
)}</span
|
||||
>
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>`;
|
||||
}
|
||||
|
||||
private async _showPicker(ev) {
|
||||
@@ -201,59 +233,69 @@ export class HaTargetPicker extends SubscribeMixin(LitElement) {
|
||||
iconPath?: string
|
||||
) {
|
||||
return html`
|
||||
<ha-chip
|
||||
class=${type}
|
||||
.id=${id}
|
||||
.leadingIcon=${iconPath ||
|
||||
entityState?.attributes.icon ||
|
||||
stateIconPath(entityState)}
|
||||
.label=${name}
|
||||
outlined
|
||||
<div
|
||||
class="mdc-chip ${classMap({
|
||||
[type]: true,
|
||||
})}"
|
||||
>
|
||||
<div slot="trailing-icon">
|
||||
${type === "entity_id"
|
||||
? ""
|
||||
: html`
|
||||
<span>
|
||||
<ha-icon-button
|
||||
class="expand-btn"
|
||||
tabindex="-1"
|
||||
role="button"
|
||||
.label=${this.hass.localize(
|
||||
"ui.components.target-picker.expand"
|
||||
)}
|
||||
.path=${mdiUnfoldMoreVertical}
|
||||
hideTitle
|
||||
.id=${id}
|
||||
.type=${type}
|
||||
@click=${this._handleExpand}
|
||||
></ha-icon-button>
|
||||
<paper-tooltip class="expand" animation-delay="0"
|
||||
>${this.hass.localize(
|
||||
`ui.components.target-picker.expand_${type}`
|
||||
)}</paper-tooltip
|
||||
>
|
||||
</span>
|
||||
`}
|
||||
<span>
|
||||
<ha-icon-button
|
||||
tabindex="-1"
|
||||
role="button"
|
||||
.label=${this.hass.localize("ui.components.target-picker.expand")}
|
||||
.path=${mdiClose}
|
||||
hideTitle
|
||||
.id=${id}
|
||||
.type=${type}
|
||||
@click=${this._handleRemove}
|
||||
></ha-icon-button>
|
||||
<paper-tooltip animation-delay="0"
|
||||
>${this.hass.localize(
|
||||
`ui.components.target-picker.remove_${type}`
|
||||
)}</paper-tooltip
|
||||
>
|
||||
${iconPath
|
||||
? html`<ha-svg-icon
|
||||
class="mdc-chip__icon mdc-chip__icon--leading"
|
||||
.path=${iconPath}
|
||||
></ha-svg-icon>`
|
||||
: ""}
|
||||
${entityState
|
||||
? html`<ha-state-icon
|
||||
class="mdc-chip__icon mdc-chip__icon--leading"
|
||||
.state=${entityState}
|
||||
></ha-state-icon>`
|
||||
: ""}
|
||||
<span role="gridcell">
|
||||
<span role="button" tabindex="0" class="mdc-chip__primary-action">
|
||||
<span class="mdc-chip__text">${name}</span>
|
||||
</span>
|
||||
</div>
|
||||
</ha-chip>
|
||||
</span>
|
||||
${type === "entity_id"
|
||||
? ""
|
||||
: html` <span role="gridcell">
|
||||
<ha-icon-button
|
||||
class="expand-btn mdc-chip__icon mdc-chip__icon--trailing"
|
||||
tabindex="-1"
|
||||
role="button"
|
||||
.label=${this.hass.localize(
|
||||
"ui.components.target-picker.expand"
|
||||
)}
|
||||
.path=${mdiUnfoldMoreVertical}
|
||||
hideTooltip
|
||||
.id=${id}
|
||||
.type=${type}
|
||||
@click=${this._handleExpand}
|
||||
></ha-icon-button>
|
||||
<paper-tooltip class="expand" animation-delay="0"
|
||||
>${this.hass.localize(
|
||||
`ui.components.target-picker.expand_${type}`
|
||||
)}</paper-tooltip
|
||||
>
|
||||
</span>`}
|
||||
<span role="gridcell">
|
||||
<ha-icon-button
|
||||
class="mdc-chip__icon mdc-chip__icon--trailing"
|
||||
tabindex="-1"
|
||||
role="button"
|
||||
.label=${this.hass.localize("ui.components.target-picker.expand")}
|
||||
.path=${mdiClose}
|
||||
hideTooltip
|
||||
.id=${id}
|
||||
.type=${type}
|
||||
@click=${this._handleRemove}
|
||||
></ha-icon-button>
|
||||
<paper-tooltip animation-delay="0"
|
||||
>${this.hass.localize(
|
||||
`ui.components.target-picker.remove_${type}`
|
||||
)}</paper-tooltip
|
||||
>
|
||||
</span>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
@@ -486,39 +528,84 @@ export class HaTargetPicker extends SubscribeMixin(LitElement) {
|
||||
|
||||
static get styles(): CSSResultGroup {
|
||||
return css`
|
||||
${unsafeCSS(chipStyles)}
|
||||
.mdc-chip {
|
||||
color: var(--primary-text-color);
|
||||
}
|
||||
.items {
|
||||
z-index: 2;
|
||||
}
|
||||
.mdc-chip-set {
|
||||
padding: 4px 0;
|
||||
}
|
||||
.mdc-chip.add {
|
||||
color: rgba(0, 0, 0, 0.87);
|
||||
}
|
||||
.mdc-chip:not(.add) {
|
||||
cursor: default;
|
||||
}
|
||||
.mdc-chip ha-icon-button {
|
||||
--mdc-icon-button-size: 24px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
outline: none;
|
||||
}
|
||||
.mdc-chip ha-icon-button ha-svg-icon {
|
||||
border-radius: 50%;
|
||||
background: var(--secondary-text-color);
|
||||
}
|
||||
.mdc-chip__icon.mdc-chip__icon--trailing {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
--mdc-icon-size: 14px;
|
||||
color: var(--secondary-text-color);
|
||||
}
|
||||
.mdc-chip__icon--leading {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
--mdc-icon-size: 20px;
|
||||
border-radius: 50%;
|
||||
padding: 6px;
|
||||
margin-left: -14px !important;
|
||||
}
|
||||
.expand-btn {
|
||||
margin-right: 0;
|
||||
}
|
||||
ha-chip {
|
||||
--ha-chip-icon-color: var(--secondary-text-color);
|
||||
--ha-chip-text-color: var(--primary-text-color);
|
||||
.mdc-chip.area_id:not(.add) {
|
||||
border: 2px solid #fed6a4;
|
||||
background: var(--card-background-color);
|
||||
}
|
||||
ha-chip:hover {
|
||||
.mdc-chip.area_id:not(.add) .mdc-chip__icon--leading,
|
||||
.mdc-chip.area_id.add {
|
||||
background: #fed6a4;
|
||||
}
|
||||
.mdc-chip.device_id:not(.add) {
|
||||
border: 2px solid #a8e1fb;
|
||||
background: var(--card-background-color);
|
||||
}
|
||||
.mdc-chip.device_id:not(.add) .mdc-chip__icon--leading,
|
||||
.mdc-chip.device_id.add {
|
||||
background: #a8e1fb;
|
||||
}
|
||||
.mdc-chip.entity_id:not(.add) {
|
||||
border: 2px solid #d2e7b9;
|
||||
background: var(--card-background-color);
|
||||
}
|
||||
.mdc-chip.entity_id:not(.add) .mdc-chip__icon--leading,
|
||||
.mdc-chip.entity_id.add {
|
||||
background: #d2e7b9;
|
||||
}
|
||||
.mdc-chip:hover {
|
||||
z-index: 5;
|
||||
}
|
||||
ha-icon-button {
|
||||
--mdc-icon-size: 18px;
|
||||
--mdc-icon-button-size: 24px;
|
||||
color: var(--secondary-text-color);
|
||||
outline: none;
|
||||
}
|
||||
ha-chip > div {
|
||||
margin-right: -8px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
}
|
||||
ha-chip.area_id {
|
||||
--ha-chip-background-color: #fed6a4;
|
||||
}
|
||||
ha-chip.device_id {
|
||||
--ha-chip-background-color: #a8e1fb;
|
||||
}
|
||||
ha-chip.entity_id {
|
||||
--ha-chip-background-color: #d2e7b9;
|
||||
}
|
||||
paper-tooltip.expand {
|
||||
min-width: 200px;
|
||||
}
|
||||
:host([disabled]) .mdc-chip {
|
||||
opacity: var(--light-disabled-opacity);
|
||||
pointer-events: none;
|
||||
}
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -194,10 +194,10 @@ export interface NumericStateCondition extends BaseCondition {
|
||||
|
||||
export interface SunCondition extends BaseCondition {
|
||||
condition: "sun";
|
||||
after_offset?: number;
|
||||
before_offset?: number;
|
||||
after?: "sunrise" | "sunset";
|
||||
before?: "sunrise" | "sunset";
|
||||
after_offset: number;
|
||||
before_offset: number;
|
||||
after: "sunrise" | "sunset";
|
||||
before: "sunrise" | "sunset";
|
||||
}
|
||||
|
||||
export interface ZoneCondition extends BaseCondition {
|
||||
|
||||
@@ -84,9 +84,6 @@ export interface ZWaveJSNodeStatus {
|
||||
ready: boolean;
|
||||
status: number;
|
||||
is_secure: boolean | string;
|
||||
is_routing: boolean | null;
|
||||
zwave_plus_version: number | null;
|
||||
highest_security_class: SecurityClass | null;
|
||||
}
|
||||
|
||||
export interface ZwaveJSNodeMetadata {
|
||||
|
||||
@@ -269,15 +269,23 @@ export class QuickBar extends LitElement {
|
||||
class="command-item"
|
||||
hasMeta
|
||||
>
|
||||
<span class="command-entry">
|
||||
<span>
|
||||
<ha-chip
|
||||
.label=${item.categoryText}
|
||||
.leadingIcon=${item.iconPath}
|
||||
hasIcon
|
||||
class="command-category ${item.categoryKey}"
|
||||
>
|
||||
</ha-chip>
|
||||
${item.primaryText}
|
||||
${item.iconPath
|
||||
? html`<ha-svg-icon
|
||||
.path=${item.iconPath}
|
||||
slot="icon"
|
||||
></ha-svg-icon>`
|
||||
: ""}
|
||||
${item.categoryText}</ha-chip
|
||||
>
|
||||
</span>
|
||||
|
||||
<span class="command-text">${item.primaryText}</span>
|
||||
</mwc-list-item>
|
||||
`;
|
||||
}
|
||||
@@ -599,10 +607,6 @@ export class QuickBar extends LitElement {
|
||||
}
|
||||
}
|
||||
|
||||
ha-chip {
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
ha-icon.entity,
|
||||
ha-svg-icon.entity {
|
||||
margin-left: 20px;
|
||||
@@ -619,6 +623,7 @@ export class QuickBar extends LitElement {
|
||||
}
|
||||
|
||||
.command-category {
|
||||
--ha-chip-icon-color: #585858;
|
||||
--ha-chip-text-color: #212121;
|
||||
}
|
||||
|
||||
@@ -634,9 +639,8 @@ export class QuickBar extends LitElement {
|
||||
--ha-chip-background-color: var(--warning-color);
|
||||
}
|
||||
|
||||
span.command-entry {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
span.command-text {
|
||||
margin-left: 8px;
|
||||
}
|
||||
|
||||
mwc-list-item {
|
||||
|
||||
@@ -371,10 +371,6 @@ export class HAFullCalendar extends LitElement {
|
||||
);
|
||||
--fc-theme-standard-border-color: var(--divider-color);
|
||||
--fc-border-color: var(--divider-color);
|
||||
--fc-page-bg-color: var(
|
||||
--ha-card-background,
|
||||
var(--card-background-color, white)
|
||||
);
|
||||
}
|
||||
|
||||
a {
|
||||
|
||||
@@ -230,7 +230,6 @@ export default class HaAutomationActionRow extends LitElement {
|
||||
"ui.panel.config.automation.editor.actions.type_select"
|
||||
)}
|
||||
.value=${getType(this.action)}
|
||||
naturalMenuWidth
|
||||
@selected=${this._typeChanged}
|
||||
>
|
||||
${this._processedTypes(this.hass.localize).map(
|
||||
|
||||
@@ -90,7 +90,6 @@ export default class HaAutomationConditionEditor extends LitElement {
|
||||
"ui.panel.config.automation.editor.conditions.type_select"
|
||||
)}
|
||||
.value=${this.condition.condition}
|
||||
naturalMenuWidth
|
||||
@selected=${this._typeChanged}
|
||||
>
|
||||
${this._processedTypes(this.hass.localize).map(
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
import { html, LitElement } from "lit";
|
||||
import { customElement, property } from "lit/decorators";
|
||||
import { fireEvent } from "../../../../../common/dom/fire_event";
|
||||
import { Condition, LogicalCondition } from "../../../../../data/automation";
|
||||
import { LogicalCondition } from "../../../../../data/automation";
|
||||
import { HomeAssistant } from "../../../../../types";
|
||||
import "../ha-automation-condition";
|
||||
import { ConditionElement } from "../ha-automation-condition-row";
|
||||
import { HaStateCondition } from "./ha-automation-condition-state";
|
||||
|
||||
@customElement("ha-automation-condition-logical")
|
||||
export class HaLogicalCondition extends LitElement implements ConditionElement {
|
||||
@@ -14,14 +13,7 @@ export class HaLogicalCondition extends LitElement implements ConditionElement {
|
||||
@property() public condition!: LogicalCondition;
|
||||
|
||||
public static get defaultConfig() {
|
||||
return {
|
||||
conditions: [
|
||||
{
|
||||
condition: "state",
|
||||
...HaStateCondition.defaultConfig,
|
||||
},
|
||||
] as Condition[],
|
||||
};
|
||||
return { conditions: [{ condition: "state" }] };
|
||||
}
|
||||
|
||||
protected render() {
|
||||
|
||||
@@ -179,7 +179,6 @@ export default class HaAutomationTriggerRow extends LitElement {
|
||||
"ui.panel.config.automation.editor.triggers.type_select"
|
||||
)}
|
||||
.value=${this.trigger.platform}
|
||||
naturalMenuWidth
|
||||
@selected=${this._typeChanged}
|
||||
>
|
||||
${this._processedTypes(this.hass.localize).map(
|
||||
|
||||
@@ -10,7 +10,7 @@ import { handleChangeEvent } from "../ha-automation-trigger-row";
|
||||
const includeDomains = ["zone"];
|
||||
|
||||
@customElement("ha-automation-trigger-geo_location")
|
||||
export class HaGeolocationTrigger extends LitElement {
|
||||
export default class HaGeolocationTrigger extends LitElement {
|
||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||
|
||||
@property({ attribute: false }) public trigger!: GeoLocationTrigger;
|
||||
@@ -19,7 +19,7 @@ export class HaGeolocationTrigger extends LitElement {
|
||||
return {
|
||||
source: "",
|
||||
zone: "",
|
||||
event: "enter" as GeoLocationTrigger["event"],
|
||||
event: "enter",
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -8,14 +8,14 @@ import "../../../../../components/ha-formfield";
|
||||
import "../../../../../components/ha-radio";
|
||||
|
||||
@customElement("ha-automation-trigger-homeassistant")
|
||||
export class HaHassTrigger extends LitElement {
|
||||
export default class HaHassTrigger extends LitElement {
|
||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||
|
||||
@property({ attribute: false }) public trigger!: HassTrigger;
|
||||
|
||||
public static get defaultConfig() {
|
||||
return {
|
||||
event: "start" as HassTrigger["event"],
|
||||
event: "start",
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ import { handleChangeEvent } from "../ha-automation-trigger-row";
|
||||
import "../../../../../components/ha-duration-input";
|
||||
|
||||
@customElement("ha-automation-trigger-numeric_state")
|
||||
export class HaNumericStateTrigger extends LitElement {
|
||||
export default class HaNumericStateTrigger extends LitElement {
|
||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||
|
||||
@property() public trigger!: NumericStateTrigger;
|
||||
|
||||
@@ -20,8 +20,7 @@ export class HaSunTrigger extends LitElement implements TriggerElement {
|
||||
|
||||
public static get defaultConfig() {
|
||||
return {
|
||||
event: "sunrise" as SunTrigger["event"],
|
||||
offset: 0,
|
||||
event: "sunrise",
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -27,7 +27,7 @@ export class HaZoneTrigger extends LitElement {
|
||||
return {
|
||||
entity_id: "",
|
||||
zone: "",
|
||||
event: "enter" as ZoneTrigger["event"],
|
||||
event: "enter",
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ class HaConfigCustomize extends LitElement {
|
||||
protected render(): TemplateResult {
|
||||
return html`
|
||||
<hass-tabs-subpage
|
||||
.hass=${this.hass}
|
||||
.hass=${this.hass}
|
||||
.narrow=${this.narrow}
|
||||
.route=${this.route}
|
||||
back-path="/config"
|
||||
|
||||
@@ -180,16 +180,10 @@ export class HaFormCustomize extends LocalizeMixin(PolymerElement) {
|
||||
this.newAttributes
|
||||
);
|
||||
attrs.forEach((attr) => {
|
||||
if (
|
||||
attr.closed ||
|
||||
attr.secondary ||
|
||||
!attr.attribute ||
|
||||
attr.value === null ||
|
||||
attr.value === undefined
|
||||
)
|
||||
if (attr.closed || attr.secondary || !attr.attribute || !attr.value)
|
||||
return;
|
||||
const value = attr.type === "json" ? JSON.parse(attr.value) : attr.value;
|
||||
if (value === null || value === undefined) return;
|
||||
if (!value) return;
|
||||
data[attr.attribute] = value;
|
||||
});
|
||||
|
||||
|
||||
@@ -57,9 +57,9 @@ export abstract class HaDeviceAutomationCard<
|
||||
<div class="content">
|
||||
<ha-chip-set
|
||||
@chip-clicked=${this._handleAutomationClicked}
|
||||
.items=${this.automations.map((automation) => ({
|
||||
label: this._localizeDeviceAutomation(this.hass, automation),
|
||||
}))}
|
||||
.items=${this.automations.map((automation) =>
|
||||
this._localizeDeviceAutomation(this.hass, automation)
|
||||
)}
|
||||
>
|
||||
</ha-chip-set>
|
||||
</div>
|
||||
|
||||
@@ -15,23 +15,18 @@ import { domainIcon } from "../../../../common/entity/domain_icon";
|
||||
import "../../../../components/entity/state-badge";
|
||||
import "../../../../components/ha-card";
|
||||
import "../../../../components/ha-icon";
|
||||
import type { LovelaceRowConfig } from "../../../lovelace/entity-rows/types";
|
||||
import type { HomeAssistant } from "../../../../types";
|
||||
import type { HuiErrorCard } from "../../../lovelace/cards/hui-error-card";
|
||||
import { HomeAssistant } from "../../../../types";
|
||||
import { HuiErrorCard } from "../../../lovelace/cards/hui-error-card";
|
||||
import { createRowElement } from "../../../lovelace/create-element/create-row-element";
|
||||
import { addEntitiesToLovelaceView } from "../../../lovelace/editor/add-entities-to-view";
|
||||
import { LovelaceRow } from "../../../lovelace/entity-rows/types";
|
||||
import { showEntityEditorDialog } from "../../entities/show-dialog-entity-editor";
|
||||
import { EntityRegistryStateEntry } from "../ha-config-device-page";
|
||||
import { computeStateName } from "../../../../common/entity/compute_state_name";
|
||||
import { stripPrefixFromEntityName } from "../../../../common/entity/strip_prefix_from_entity_name";
|
||||
|
||||
@customElement("ha-device-entities-card")
|
||||
export class HaDeviceEntitiesCard extends LitElement {
|
||||
@property() public header!: string;
|
||||
|
||||
@property() public deviceName!: string;
|
||||
|
||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||
|
||||
@property() public entities!: EntityRegistryStateEntry[];
|
||||
@@ -124,21 +119,9 @@ export class HaDeviceEntitiesCard extends LitElement {
|
||||
}
|
||||
|
||||
private _renderEntity(entry: EntityRegistryStateEntry): TemplateResult {
|
||||
const config: LovelaceRowConfig = {
|
||||
entity: entry.entity_id,
|
||||
};
|
||||
|
||||
const element = createRowElement(config);
|
||||
const element = createRowElement({ entity: entry.entity_id });
|
||||
if (this.hass) {
|
||||
element.hass = this.hass;
|
||||
const state = this.hass.states[entry.entity_id];
|
||||
const name = stripPrefixFromEntityName(
|
||||
computeStateName(state),
|
||||
`${this.deviceName} `.toLowerCase()
|
||||
);
|
||||
if (name) {
|
||||
config.name = name;
|
||||
}
|
||||
}
|
||||
// @ts-ignore
|
||||
element.entry = entry;
|
||||
@@ -148,11 +131,7 @@ export class HaDeviceEntitiesCard extends LitElement {
|
||||
|
||||
private _renderEntry(entry: EntityRegistryStateEntry): TemplateResult {
|
||||
return html`
|
||||
<paper-icon-item
|
||||
class="disabled-entry"
|
||||
.entry=${entry}
|
||||
@click=${this._openEditEntry}
|
||||
>
|
||||
<paper-icon-item .entry=${entry} @click=${this._openEditEntry}>
|
||||
<ha-svg-icon
|
||||
slot="item-icon"
|
||||
.path=${domainIcon(computeDomain(entry.entity_id))}
|
||||
@@ -209,9 +188,6 @@ export class HaDeviceEntitiesCard extends LitElement {
|
||||
.disabled-entry {
|
||||
color: var(--secondary-text-color);
|
||||
}
|
||||
#entities {
|
||||
margin-top: -24px; /* match the spacing between card title and content of the device info card above it */
|
||||
}
|
||||
#entities > * {
|
||||
margin: 8px 16px 8px 8px;
|
||||
}
|
||||
@@ -220,9 +196,8 @@ export class HaDeviceEntitiesCard extends LitElement {
|
||||
}
|
||||
paper-icon-item {
|
||||
min-height: 40px;
|
||||
padding: 0 16px;
|
||||
padding: 0 8px;
|
||||
cursor: pointer;
|
||||
--paper-item-icon-width: 48px;
|
||||
}
|
||||
.name {
|
||||
font-size: 14px;
|
||||
|
||||
+4
-25
@@ -18,7 +18,6 @@ import {
|
||||
nodeStatus,
|
||||
ZWaveJSNodeStatus,
|
||||
ZWaveJSNodeIdentifiers,
|
||||
SecurityClass,
|
||||
} from "../../../../../../data/zwave_js";
|
||||
import { haStyle } from "../../../../../../resources/styles";
|
||||
import { HomeAssistant } from "../../../../../../types";
|
||||
@@ -118,33 +117,13 @@ export class HaDeviceInfoZWaveJS extends LitElement {
|
||||
: this.hass.localize("ui.common.no")}
|
||||
</div>
|
||||
<div>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.zwave_js.device_info.highest_security"
|
||||
)}:
|
||||
${this._node.highest_security_class !== null
|
||||
? this.hass.localize(
|
||||
`ui.panel.config.zwave_js.security_classes.${
|
||||
SecurityClass[this._node.highest_security_class]
|
||||
}.title`
|
||||
)
|
||||
${this.hass.localize("ui.panel.config.zwave_js.device_info.is_secure")}:
|
||||
${this._node.is_secure === true
|
||||
? this.hass.localize("ui.common.yes")
|
||||
: this._node.is_secure === false
|
||||
? this.hass.localize(
|
||||
"ui.panel.config.zwave_js.security_classes.none.title"
|
||||
)
|
||||
? this.hass.localize("ui.common.no")
|
||||
: this.hass.localize("ui.panel.config.zwave_js.device_info.unknown")}
|
||||
</div>
|
||||
<div>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.zwave_js.device_info.zwave_plus"
|
||||
)}:
|
||||
${this._node.zwave_plus_version
|
||||
? this.hass.localize(
|
||||
"ui.panel.config.zwave_js.device_info.zwave_plus_version",
|
||||
"version",
|
||||
this._node.zwave_plus_version
|
||||
)
|
||||
: this.hass.localize("ui.common.no")}
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
|
||||
@@ -179,7 +179,6 @@ export class HaConfigDevicePage extends LitElement {
|
||||
`;
|
||||
}
|
||||
|
||||
const deviceName = computeDeviceName(device, this.hass);
|
||||
const integrations = this._integrations(device, this.entries);
|
||||
const entities = this._entities(this.deviceId, this.entities);
|
||||
const entitiesByCategory = this._entitiesByCategory(entities);
|
||||
@@ -205,7 +204,9 @@ export class HaConfigDevicePage extends LitElement {
|
||||
${
|
||||
this.narrow
|
||||
? html`
|
||||
<span slot="header">${deviceName}</span>
|
||||
<span slot="header">
|
||||
${computeDeviceName(device, this.hass)}
|
||||
</span>
|
||||
<ha-icon-button
|
||||
slot="toolbar-icon"
|
||||
.path=${mdiPencil}
|
||||
@@ -229,7 +230,7 @@ export class HaConfigDevicePage extends LitElement {
|
||||
: html`
|
||||
<div class="header-name">
|
||||
<div>
|
||||
<h1>${deviceName}</h1>
|
||||
<h1>${computeDeviceName(device, this.hass)}</h1>
|
||||
${area
|
||||
? html`
|
||||
<a href="/config/areas/area/${area.area_id}"
|
||||
@@ -352,7 +353,6 @@ export class HaConfigDevicePage extends LitElement {
|
||||
.header=${this.hass.localize(
|
||||
`ui.panel.config.devices.entities.${category}`
|
||||
)}
|
||||
.deviceName=${deviceName}
|
||||
.entities=${entitiesByCategory[category]}
|
||||
.showDisabled=${device.disabled_by !== null}
|
||||
>
|
||||
@@ -420,7 +420,7 @@ export class HaConfigDevicePage extends LitElement {
|
||||
: "";
|
||||
})
|
||||
: html`
|
||||
<div class="card-content">
|
||||
<paper-item class="no-link">
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.devices.add_prompt",
|
||||
"name",
|
||||
@@ -428,7 +428,7 @@ export class HaConfigDevicePage extends LitElement {
|
||||
"ui.panel.config.devices.automation.automations"
|
||||
)
|
||||
)}
|
||||
</div>
|
||||
</paper-item>
|
||||
`}
|
||||
</ha-card>
|
||||
`
|
||||
@@ -502,7 +502,7 @@ export class HaConfigDevicePage extends LitElement {
|
||||
: "";
|
||||
})
|
||||
: html`
|
||||
<div class="card-content">
|
||||
<paper-item class="no-link">
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.devices.add_prompt",
|
||||
"name",
|
||||
@@ -510,7 +510,7 @@ export class HaConfigDevicePage extends LitElement {
|
||||
"ui.panel.config.devices.scene.scenes"
|
||||
)
|
||||
)}
|
||||
</div>
|
||||
</paper-item>
|
||||
`
|
||||
}
|
||||
</ha-card>
|
||||
@@ -558,7 +558,7 @@ export class HaConfigDevicePage extends LitElement {
|
||||
: "";
|
||||
})
|
||||
: html`
|
||||
<div class="card-content">
|
||||
<paper-item class="no-link">
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.devices.add_prompt",
|
||||
"name",
|
||||
@@ -566,7 +566,7 @@ export class HaConfigDevicePage extends LitElement {
|
||||
"ui.panel.config.devices.script.scripts"
|
||||
)
|
||||
)}
|
||||
</div>
|
||||
</paper-item>
|
||||
`}
|
||||
</ha-card>
|
||||
`
|
||||
@@ -953,11 +953,19 @@ export class HaConfigDevicePage extends LitElement {
|
||||
font-size: var(--paper-font-body1_-_font-size);
|
||||
}
|
||||
|
||||
paper-item.no-link {
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
a {
|
||||
text-decoration: none;
|
||||
color: var(--primary-color);
|
||||
}
|
||||
|
||||
ha-card {
|
||||
padding-bottom: 8px;
|
||||
}
|
||||
|
||||
ha-card a {
|
||||
color: var(--primary-text-color);
|
||||
}
|
||||
|
||||
+2
-2
@@ -202,12 +202,12 @@ class DialogZWaveJSAddNode extends LitElement {
|
||||
(securityClass) => html`<ha-formfield
|
||||
.label=${html`<b
|
||||
>${this.hass.localize(
|
||||
`ui.panel.config.zwave_js.security_classes.${SecurityClass[securityClass]}.title`
|
||||
`ui.panel.config.zwave_js.add_node.security_classes.${SecurityClass[securityClass]}.title`
|
||||
)}</b
|
||||
>
|
||||
<div class="secondary">
|
||||
${this.hass.localize(
|
||||
`ui.panel.config.zwave_js.security_classes.${SecurityClass[securityClass]}.description`
|
||||
`ui.panel.config.zwave_js.add_node.security_classes.${SecurityClass[securityClass]}.description`
|
||||
)}
|
||||
</div>`}
|
||||
>
|
||||
|
||||
@@ -8,18 +8,17 @@ import {
|
||||
PropertyValues,
|
||||
TemplateResult,
|
||||
} from "lit";
|
||||
import { customElement, property, query, state } from "lit/decorators";
|
||||
import { customElement, property, state, query } from "lit/decorators";
|
||||
import { classMap } from "lit/directives/class-map";
|
||||
import { applyThemesOnElement } from "../../../common/dom/apply_themes_on_element";
|
||||
import { fireEvent } from "../../../common/dom/fire_event";
|
||||
import { alarmPanelIcon } from "../../../common/entity/alarm_panel_icon";
|
||||
import "../../../components/ha-card";
|
||||
import "../../../components/ha-chip";
|
||||
import "../../../components/ha-label-badge";
|
||||
import {
|
||||
callAlarmAction,
|
||||
FORMAT_NUMBER,
|
||||
} from "../../../data/alarm_control_panel";
|
||||
import { UNAVAILABLE } from "../../../data/entity";
|
||||
import type { HomeAssistant } from "../../../types";
|
||||
import { findEntities } from "../common/find-entities";
|
||||
import { createEntityNotFoundWarning } from "../components/hui-warning";
|
||||
@@ -145,22 +144,19 @@ class HuiAlarmPanelCard extends LitElement implements LovelaceCard {
|
||||
`;
|
||||
}
|
||||
|
||||
const stateLabel = this._stateDisplay(stateObj.state);
|
||||
|
||||
return html`
|
||||
<ha-card>
|
||||
<h1 class="card-header">
|
||||
${this._config.name ||
|
||||
stateObj.attributes.friendly_name ||
|
||||
stateLabel}
|
||||
<ha-chip
|
||||
.leadingIcon=${alarmPanelIcon(stateObj.state)}
|
||||
.label=${stateLabel}
|
||||
class=${classMap({ [stateObj.state]: true })}
|
||||
@click=${this._handleMoreInfo}
|
||||
>
|
||||
</ha-chip>
|
||||
</h1>
|
||||
<ha-card
|
||||
.header=${this._config.name ||
|
||||
stateObj.attributes.friendly_name ||
|
||||
this._stateDisplay(stateObj.state)}
|
||||
>
|
||||
<ha-label-badge
|
||||
class=${classMap({ [stateObj.state]: true })}
|
||||
.label=${this._stateIconLabel(stateObj.state)}
|
||||
@click=${this._handleMoreInfo}
|
||||
>
|
||||
<ha-svg-icon .path=${alarmPanelIcon(stateObj.state)}></ha-svg-icon>
|
||||
</ha-label-badge>
|
||||
<div id="armActions" class="actions">
|
||||
${(stateObj.state === "disarmed"
|
||||
? this._config.states!
|
||||
@@ -219,16 +215,23 @@ class HuiAlarmPanelCard extends LitElement implements LovelaceCard {
|
||||
`;
|
||||
}
|
||||
|
||||
private _stateIconLabel(entityState: string): string {
|
||||
const stateLabel = entityState.split("_").pop();
|
||||
return stateLabel === "disarmed" ||
|
||||
stateLabel === "triggered" ||
|
||||
!stateLabel
|
||||
? ""
|
||||
: this._stateDisplay(entityState);
|
||||
}
|
||||
|
||||
private _actionDisplay(entityState: string): string {
|
||||
return this.hass!.localize(`ui.card.alarm_control_panel.${entityState}`);
|
||||
}
|
||||
|
||||
private _stateDisplay(entityState: string): string {
|
||||
return entityState === UNAVAILABLE
|
||||
? this.hass!.localize("state.default.unavailable")
|
||||
: this.hass!.localize(
|
||||
`component.alarm_control_panel.state._.${entityState}`
|
||||
) || entityState;
|
||||
return this.hass!.localize(
|
||||
`component.alarm_control_panel.state._.${entityState}`
|
||||
);
|
||||
}
|
||||
|
||||
private _handlePadClick(e: MouseEvent): void {
|
||||
@@ -270,20 +273,15 @@ class HuiAlarmPanelCard extends LitElement implements LovelaceCard {
|
||||
--alarm-state-color: var(--alarm-color-armed);
|
||||
}
|
||||
|
||||
ha-chip {
|
||||
--ha-chip-background-color: var(--alarm-state-color);
|
||||
--ha-chip-text-color: var(--text-primary-color);
|
||||
line-height: initial;
|
||||
}
|
||||
|
||||
.card-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.unavailable {
|
||||
--alarm-state-color: var(--state-unavailable-color);
|
||||
ha-label-badge {
|
||||
--ha-label-badge-color: var(--alarm-state-color);
|
||||
--label-badge-text-color: var(--alarm-state-color);
|
||||
--label-badge-background-color: var(--card-background-color);
|
||||
color: var(--alarm-state-color);
|
||||
position: absolute;
|
||||
right: 12px;
|
||||
top: 8px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.disarmed {
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { mdiArrowDown, mdiArrowUp } from "@mdi/js";
|
||||
import {
|
||||
css,
|
||||
CSSResultGroup,
|
||||
@@ -7,6 +8,7 @@ import {
|
||||
TemplateResult,
|
||||
} from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { classMap } from "lit/directives/class-map";
|
||||
import { ifDefined } from "lit/directives/if-defined";
|
||||
import { applyThemesOnElement } from "../../../common/dom/apply_themes_on_element";
|
||||
import { fireEvent } from "../../../common/dom/fire_event";
|
||||
@@ -16,10 +18,12 @@ import { computeStateDomain } from "../../../common/entity/compute_state_domain"
|
||||
import { computeStateName } from "../../../common/entity/compute_state_name";
|
||||
import { isValidEntityId } from "../../../common/entity/valid_entity_id";
|
||||
import { formatNumber } from "../../../common/number/format_number";
|
||||
import { round } from "../../../common/number/round";
|
||||
import { iconColorCSS } from "../../../common/style/icon_color_css";
|
||||
import "../../../components/ha-card";
|
||||
import "../../../components/ha-icon";
|
||||
import { UNAVAILABLE_STATES } from "../../../data/entity";
|
||||
import { fetchRecent } from "../../../data/history";
|
||||
import { HomeAssistant } from "../../../types";
|
||||
import { formatAttributeValue } from "../../../util/hass-attributes-util";
|
||||
import { computeCardSize } from "../common/compute-card-size";
|
||||
@@ -66,8 +70,14 @@ export class HuiEntityCard extends LitElement implements LovelaceCard {
|
||||
|
||||
@state() private _config?: EntityCardConfig;
|
||||
|
||||
@state() private _lastState?: number;
|
||||
|
||||
private _footerElement?: HuiErrorCard | LovelaceHeaderFooter;
|
||||
|
||||
private _date?: Date;
|
||||
|
||||
private _fetching = false;
|
||||
|
||||
public setConfig(config: EntityCardConfig): void {
|
||||
if (!config.entity) {
|
||||
throw new Error("Entity must be specified");
|
||||
@@ -76,7 +86,10 @@ export class HuiEntityCard extends LitElement implements LovelaceCard {
|
||||
throw new Error("Invalid entity");
|
||||
}
|
||||
|
||||
this._config = config;
|
||||
this._config = {
|
||||
hours_to_show: 24,
|
||||
...config,
|
||||
};
|
||||
|
||||
if (this._config.footer) {
|
||||
this._footerElement = createHeaderFooterElement(this._config.footer);
|
||||
@@ -115,23 +128,38 @@ export class HuiEntityCard extends LitElement implements LovelaceCard {
|
||||
: !UNAVAILABLE_STATES.includes(stateObj.state);
|
||||
|
||||
const name = this._config.name || computeStateName(stateObj);
|
||||
const trend = this._lastState
|
||||
? round((Number(stateObj.state) / this._lastState) * 100, 0)
|
||||
: undefined;
|
||||
|
||||
return html`
|
||||
<ha-card @click=${this._handleClick} tabindex="0">
|
||||
<div class="header">
|
||||
<div class="name" .title=${name}>${name}</div>
|
||||
<div class="icon">
|
||||
<ha-state-icon
|
||||
.icon=${this._config.icon}
|
||||
.state=${stateObj}
|
||||
data-domain=${ifDefined(
|
||||
this._config.state_color ||
|
||||
(domain === "light" && this._config.state_color !== false)
|
||||
? domain
|
||||
: undefined
|
||||
)}
|
||||
data-state=${stateObj ? computeActiveState(stateObj) : ""}
|
||||
></ha-state-icon>
|
||||
${this._config.show_trend && trend
|
||||
? html`
|
||||
<div class="trend ${classMap({ error: trend < 100 })}">
|
||||
<ha-svg-icon
|
||||
.path=${trend < 100 ? mdiArrowDown : mdiArrowUp}
|
||||
></ha-svg-icon>
|
||||
${trend} %
|
||||
</div>
|
||||
`
|
||||
: html`
|
||||
<ha-state-icon
|
||||
.icon=${this._config.icon}
|
||||
.state=${stateObj}
|
||||
data-domain=${ifDefined(
|
||||
this._config.state_color ||
|
||||
(domain === "light" &&
|
||||
this._config.state_color !== false)
|
||||
? domain
|
||||
: undefined
|
||||
)}
|
||||
data-state=${stateObj ? computeActiveState(stateObj) : ""}
|
||||
></ha-state-icon>
|
||||
`}
|
||||
</div>
|
||||
</div>
|
||||
<div class="info">
|
||||
@@ -177,7 +205,11 @@ export class HuiEntityCard extends LitElement implements LovelaceCard {
|
||||
|
||||
protected updated(changedProps: PropertyValues) {
|
||||
super.updated(changedProps);
|
||||
if (!this._config || !this.hass) {
|
||||
if (
|
||||
!this._config ||
|
||||
!this.hass ||
|
||||
(this._fetching && !changedProps.has("_config"))
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -194,12 +226,46 @@ export class HuiEntityCard extends LitElement implements LovelaceCard {
|
||||
) {
|
||||
applyThemesOnElement(this, this.hass.themes, this._config!.theme);
|
||||
}
|
||||
|
||||
if (changedProps.has("_config")) {
|
||||
if (!oldConfig || oldConfig.entity !== this._config.entity) {
|
||||
this._lastState = undefined;
|
||||
}
|
||||
this._getStateHistory();
|
||||
} else if (Date.now() - this._date!.getTime() >= 60000) {
|
||||
this._getStateHistory();
|
||||
}
|
||||
}
|
||||
|
||||
private _handleClick(): void {
|
||||
fireEvent(this, "hass-more-info", { entityId: this._config!.entity });
|
||||
}
|
||||
|
||||
private async _getStateHistory(): Promise<void> {
|
||||
if (this._fetching) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._fetching = true;
|
||||
|
||||
const now = new Date();
|
||||
const startTime = new Date(
|
||||
new Date().setHours(now.getHours() - this._config!.hours_to_show!)
|
||||
);
|
||||
|
||||
const stateHistory = await fetchRecent(
|
||||
this.hass!,
|
||||
this._config!.entity,
|
||||
startTime,
|
||||
startTime
|
||||
);
|
||||
|
||||
this._lastState = Number(stateHistory[0][0].state);
|
||||
|
||||
this._date = now;
|
||||
this._fetching = false;
|
||||
}
|
||||
|
||||
static get styles(): CSSResultGroup {
|
||||
return [
|
||||
iconColorCSS,
|
||||
@@ -234,6 +300,17 @@ export class HuiEntityCard extends LitElement implements LovelaceCard {
|
||||
line-height: 40px;
|
||||
}
|
||||
|
||||
.trend {
|
||||
font-size: 16px;
|
||||
color: var(--success-color);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.trend.error {
|
||||
color: var(--error-color);
|
||||
}
|
||||
|
||||
.info {
|
||||
padding: 0px 16px 16px;
|
||||
margin-top: -4px;
|
||||
|
||||
@@ -171,7 +171,6 @@ export class HuiMarkdownCard extends LitElement implements LovelaceCard {
|
||||
}
|
||||
ha-markdown {
|
||||
padding: 0 16px 16px;
|
||||
word-wrap: break-word;
|
||||
}
|
||||
ha-markdown.no-header {
|
||||
padding-top: 16px;
|
||||
|
||||
@@ -51,6 +51,7 @@ class HuiSensorCard extends HuiEntityCard {
|
||||
|
||||
const entityCardConfig: EntityCardConfig = {
|
||||
...cardConfig,
|
||||
hours_to_show,
|
||||
type: "entity",
|
||||
};
|
||||
|
||||
|
||||
@@ -40,6 +40,8 @@ export interface EntityCardConfig extends LovelaceCardConfig {
|
||||
unit?: string;
|
||||
theme?: string;
|
||||
state_color?: boolean;
|
||||
hours_to_show?: number;
|
||||
show_trend?: boolean;
|
||||
}
|
||||
|
||||
export interface EntitiesCardEntityConfig extends EntityConfig {
|
||||
@@ -357,6 +359,7 @@ export interface SensorCardConfig extends LovelaceCardConfig {
|
||||
detail?: number;
|
||||
theme?: string;
|
||||
hours_to_show?: number;
|
||||
show_trend?: boolean;
|
||||
limits?: {
|
||||
min?: number;
|
||||
max?: number;
|
||||
|
||||
@@ -3,7 +3,6 @@ import { computeDomain } from "../../../common/entity/compute_domain";
|
||||
import { computeStateDomain } from "../../../common/entity/compute_state_domain";
|
||||
import { computeStateName } from "../../../common/entity/compute_state_name";
|
||||
import { splitByGroups } from "../../../common/entity/split_by_groups";
|
||||
import { stripPrefixFromEntityName } from "../../../common/entity/strip_prefix_from_entity_name";
|
||||
import { stringCompare } from "../../../common/string/compare";
|
||||
import { LocalizeFunc } from "../../../common/translations/localize";
|
||||
import type { AreaRegistryEntry } from "../../../data/area_registry";
|
||||
@@ -93,7 +92,7 @@ export const computeCards = (
|
||||
const entities: Array<string | LovelaceRowConfig> = [];
|
||||
|
||||
const titlePrefix = entityCardOptions.title
|
||||
? `${entityCardOptions.title} `.toLowerCase()
|
||||
? `${entityCardOptions.title} `
|
||||
: undefined;
|
||||
|
||||
for (const [entityId, stateObj] of states) {
|
||||
@@ -154,18 +153,16 @@ export const computeCards = (
|
||||
) {
|
||||
// Do nothing.
|
||||
} else {
|
||||
let name: string | undefined;
|
||||
let name: string;
|
||||
const entityConf =
|
||||
titlePrefix &&
|
||||
stateObj &&
|
||||
// eslint-disable-next-line no-cond-assign
|
||||
(name = stripPrefixFromEntityName(
|
||||
computeStateName(stateObj),
|
||||
titlePrefix
|
||||
))
|
||||
(name = computeStateName(stateObj)) !== titlePrefix &&
|
||||
name.startsWith(titlePrefix)
|
||||
? {
|
||||
entity: entityId,
|
||||
name,
|
||||
name: adjustName(name.substr(titlePrefix.length)),
|
||||
}
|
||||
: entityId;
|
||||
|
||||
@@ -184,6 +181,15 @@ export const computeCards = (
|
||||
return cards;
|
||||
};
|
||||
|
||||
const hasUpperCase = (str: string): boolean => str.toLowerCase() !== str;
|
||||
|
||||
const adjustName = (name: string): string =>
|
||||
// If first word already has an upper case letter (e.g. from brand name)
|
||||
// leave as-is, otherwise capitalize the first word.
|
||||
hasUpperCase(name.substr(0, name.indexOf(" ")))
|
||||
? name
|
||||
: name[0].toUpperCase() + name.slice(1);
|
||||
|
||||
const computeDefaultViewStates = (
|
||||
entities: HassEntities,
|
||||
entityEntries: EntityRegistryEntry[]
|
||||
|
||||
@@ -10,6 +10,10 @@ import {
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { guard } from "lit/directives/guard";
|
||||
import type { SortableEvent } from "sortablejs";
|
||||
import Sortable, {
|
||||
AutoScroll,
|
||||
OnSpill,
|
||||
} from "sortablejs/modular/sortable.core.esm";
|
||||
import { fireEvent } from "../../../common/dom/fire_event";
|
||||
import "../../../components/entity/ha-entity-picker";
|
||||
import type { HaEntityPicker } from "../../../components/entity/ha-entity-picker";
|
||||
@@ -18,8 +22,6 @@ import { sortableStyles } from "../../../resources/ha-sortable-style";
|
||||
import { HomeAssistant } from "../../../types";
|
||||
import { EntityConfig } from "../entity-rows/types";
|
||||
|
||||
let Sortable;
|
||||
|
||||
@customElement("hui-entity-editor")
|
||||
export class HuiEntityEditor extends LitElement {
|
||||
@property({ attribute: false }) protected hass?: HomeAssistant;
|
||||
@@ -32,7 +34,7 @@ export class HuiEntityEditor extends LitElement {
|
||||
|
||||
@state() private _renderEmptySortable = false;
|
||||
|
||||
private _sortable?;
|
||||
private _sortable?: Sortable;
|
||||
|
||||
public connectedCallback() {
|
||||
super.connectedCallback();
|
||||
@@ -84,6 +86,11 @@ export class HuiEntityEditor extends LitElement {
|
||||
`;
|
||||
}
|
||||
|
||||
protected firstUpdated(): void {
|
||||
Sortable.mount(OnSpill);
|
||||
Sortable.mount(new AutoScroll());
|
||||
}
|
||||
|
||||
protected updated(changedProps: PropertyValues): void {
|
||||
super.updated(changedProps);
|
||||
|
||||
@@ -121,17 +128,7 @@ export class HuiEntityEditor extends LitElement {
|
||||
this._renderEmptySortable = false;
|
||||
}
|
||||
|
||||
private async _createSortable() {
|
||||
if (!Sortable) {
|
||||
const sortableImport = await import(
|
||||
"sortablejs/modular/sortable.core.esm"
|
||||
);
|
||||
|
||||
Sortable = sortableImport.Sortable;
|
||||
Sortable.mount(sortableImport.OnSpill);
|
||||
Sortable.mount(sortableImport.AutoScroll());
|
||||
}
|
||||
|
||||
private _createSortable() {
|
||||
this._sortable = new Sortable(this.shadowRoot!.querySelector(".entities"), {
|
||||
animation: 150,
|
||||
fallbackClass: "sortable-fallback",
|
||||
|
||||
@@ -75,8 +75,6 @@ export class HuiDialogEditCard
|
||||
|
||||
@state() private _dirty = false;
|
||||
|
||||
@state() private _isEscapeEnabled = true;
|
||||
|
||||
public async showDialog(params: EditCardDialogParams): Promise<void> {
|
||||
this._params = params;
|
||||
this._GUImode = true;
|
||||
@@ -95,9 +93,6 @@ export class HuiDialogEditCard
|
||||
}
|
||||
|
||||
public closeDialog(): boolean {
|
||||
this._isEscapeEnabled = true;
|
||||
window.removeEventListener("dialog-closed", this._enableEscapeKeyClose);
|
||||
window.removeEventListener("hass-more-info", this._disableEscapeKeyClose);
|
||||
if (this._dirty) {
|
||||
this._confirmCancel();
|
||||
return false;
|
||||
@@ -130,16 +125,6 @@ export class HuiDialogEditCard
|
||||
}
|
||||
}
|
||||
|
||||
private _enableEscapeKeyClose = (ev: any) => {
|
||||
if (ev.detail.dialog === "ha-more-info-dialog") {
|
||||
this._isEscapeEnabled = true;
|
||||
}
|
||||
};
|
||||
|
||||
private _disableEscapeKeyClose = () => {
|
||||
this._isEscapeEnabled = false;
|
||||
};
|
||||
|
||||
protected render(): TemplateResult {
|
||||
if (!this._params) {
|
||||
return html``;
|
||||
@@ -172,7 +157,6 @@ export class HuiDialogEditCard
|
||||
<ha-dialog
|
||||
open
|
||||
scrimClickAction
|
||||
.escapeKeyAction=${this._isEscapeEnabled ? undefined : ""}
|
||||
@keydown=${this._ignoreKeydown}
|
||||
@closed=${this._cancel}
|
||||
@opened=${this._opened}
|
||||
@@ -296,8 +280,6 @@ export class HuiDialogEditCard
|
||||
}
|
||||
|
||||
private _opened() {
|
||||
window.addEventListener("dialog-closed", this._enableEscapeKeyClose);
|
||||
window.addEventListener("hass-more-info", this._disableEscapeKeyClose);
|
||||
this._cardEditorEl?.focusYamlEditor();
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,15 @@
|
||||
import "@polymer/paper-input/paper-input";
|
||||
import { CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { assert, assign, boolean, object, optional, string } from "superstruct";
|
||||
import {
|
||||
assert,
|
||||
assign,
|
||||
boolean,
|
||||
number,
|
||||
object,
|
||||
optional,
|
||||
string,
|
||||
} from "superstruct";
|
||||
import { fireEvent } from "../../../../common/dom/fire_event";
|
||||
import { computeDomain } from "../../../../common/entity/compute_domain";
|
||||
import { domainIcon } from "../../../../common/entity/domain_icon";
|
||||
@@ -29,6 +37,8 @@ const cardConfigStruct = assign(
|
||||
theme: optional(string()),
|
||||
state_color: optional(boolean()),
|
||||
footer: optional(headerFooterConfigStructs),
|
||||
hours_to_show: optional(number()),
|
||||
show_trend: optional(boolean()),
|
||||
})
|
||||
);
|
||||
|
||||
@@ -74,6 +84,14 @@ export class HuiEntityCardEditor
|
||||
return this._config!.theme || "";
|
||||
}
|
||||
|
||||
get _hours_to_show(): number | string {
|
||||
return this._config!.hours_to_show || "24";
|
||||
}
|
||||
|
||||
get _show_trend(): boolean {
|
||||
return this._config!.show_trend || false;
|
||||
}
|
||||
|
||||
protected render(): TemplateResult {
|
||||
if (!this.hass || !this._config) {
|
||||
return html``;
|
||||
@@ -167,6 +185,36 @@ export class HuiEntityCardEditor
|
||||
</ha-switch>
|
||||
</ha-formfield>
|
||||
</div>
|
||||
|
||||
<div class="side-by-side">
|
||||
<ha-formfield
|
||||
.label=${this.hass.localize(
|
||||
"ui.panel.lovelace.editor.card.entity.show_trend"
|
||||
)}
|
||||
>
|
||||
<ha-switch
|
||||
.checked=${this._show_trend}
|
||||
.configValue=${"show_trend"}
|
||||
@change=${this._valueChanged}
|
||||
></ha-switch>
|
||||
</ha-formfield>
|
||||
${this._show_trend
|
||||
? html`
|
||||
<paper-input
|
||||
.label="${this.hass.localize(
|
||||
"ui.panel.lovelace.editor.card.generic.hours_to_show"
|
||||
)} (${this.hass.localize(
|
||||
"ui.panel.lovelace.editor.card.config.optional"
|
||||
)})"
|
||||
type="number"
|
||||
.value=${this._hours_to_show}
|
||||
min="1"
|
||||
.configValue=${"hours_to_show"}
|
||||
@value-changed=${this._valueChanged}
|
||||
></paper-input>
|
||||
`
|
||||
: ""}
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
@@ -4,7 +4,15 @@ import "@polymer/paper-item/paper-item";
|
||||
import "@polymer/paper-listbox/paper-listbox";
|
||||
import { CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { assert, assign, number, object, optional, string } from "superstruct";
|
||||
import {
|
||||
assert,
|
||||
assign,
|
||||
boolean,
|
||||
number,
|
||||
object,
|
||||
optional,
|
||||
string,
|
||||
} from "superstruct";
|
||||
import { fireEvent } from "../../../../common/dom/fire_event";
|
||||
import { computeDomain } from "../../../../common/entity/compute_domain";
|
||||
import { domainIcon } from "../../../../common/entity/domain_icon";
|
||||
@@ -31,6 +39,7 @@ const cardConfigStruct = assign(
|
||||
detail: optional(number()),
|
||||
theme: optional(string()),
|
||||
hours_to_show: optional(number()),
|
||||
show_trend: optional(boolean()),
|
||||
})
|
||||
);
|
||||
|
||||
@@ -82,6 +91,10 @@ export class HuiSensorCardEditor
|
||||
return this._config!.hours_to_show || "24";
|
||||
}
|
||||
|
||||
get _show_trend(): boolean {
|
||||
return this._config!.show_trend || false;
|
||||
}
|
||||
|
||||
protected render(): TemplateResult {
|
||||
if (!this.hass || !this._config) {
|
||||
return html``;
|
||||
@@ -193,6 +206,17 @@ export class HuiSensorCardEditor
|
||||
@value-changed=${this._valueChanged}
|
||||
></paper-input>
|
||||
</div>
|
||||
<ha-formfield
|
||||
label=${this.hass.localize(
|
||||
"ui.panel.lovelace.editor.card.sensor.show_trend"
|
||||
)}
|
||||
>
|
||||
<ha-switch
|
||||
.checked=${this._show_trend}
|
||||
.configValue=${"show_trend"}
|
||||
@change=${this._change}
|
||||
></ha-switch>
|
||||
</ha-formfield>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
@@ -202,15 +226,21 @@ export class HuiSensorCardEditor
|
||||
return;
|
||||
}
|
||||
|
||||
const value = (ev.target! as EditorTarget).checked ? 2 : 1;
|
||||
const target = ev.target! as EditorTarget;
|
||||
const value =
|
||||
target.configValue === "detail"
|
||||
? (ev.target! as EditorTarget).checked
|
||||
? 2
|
||||
: 1
|
||||
: target.checked;
|
||||
|
||||
if (this._detail === value) {
|
||||
if (this[`_${target.configValue}`] === value) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._config = {
|
||||
...this._config,
|
||||
detail: value,
|
||||
[target.configValue!]: value,
|
||||
};
|
||||
|
||||
fireEvent(this, "config-changed", { config: this._config });
|
||||
|
||||
@@ -10,6 +10,10 @@ import {
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { guard } from "lit/directives/guard";
|
||||
import type { SortableEvent } from "sortablejs";
|
||||
import Sortable, {
|
||||
AutoScroll,
|
||||
OnSpill,
|
||||
} from "sortablejs/modular/sortable.core.esm";
|
||||
import { fireEvent } from "../../../common/dom/fire_event";
|
||||
import "../../../components/entity/ha-entity-picker";
|
||||
import type { HaEntityPicker } from "../../../components/entity/ha-entity-picker";
|
||||
@@ -19,8 +23,6 @@ import { sortableStyles } from "../../../resources/ha-sortable-style";
|
||||
import { HomeAssistant } from "../../../types";
|
||||
import { EntityConfig, LovelaceRowConfig } from "../entity-rows/types";
|
||||
|
||||
let Sortable;
|
||||
|
||||
declare global {
|
||||
interface HASSDomEvents {
|
||||
"entities-changed": {
|
||||
@@ -41,7 +43,7 @@ export class HuiEntitiesCardRowEditor extends LitElement {
|
||||
|
||||
@state() private _renderEmptySortable = false;
|
||||
|
||||
private _sortable?;
|
||||
private _sortable?: Sortable;
|
||||
|
||||
public connectedCallback() {
|
||||
super.connectedCallback();
|
||||
@@ -132,6 +134,11 @@ export class HuiEntitiesCardRowEditor extends LitElement {
|
||||
`;
|
||||
}
|
||||
|
||||
protected firstUpdated(): void {
|
||||
Sortable.mount(OnSpill);
|
||||
Sortable.mount(new AutoScroll());
|
||||
}
|
||||
|
||||
protected updated(changedProps: PropertyValues): void {
|
||||
super.updated(changedProps);
|
||||
|
||||
@@ -169,17 +176,7 @@ export class HuiEntitiesCardRowEditor extends LitElement {
|
||||
this._renderEmptySortable = false;
|
||||
}
|
||||
|
||||
private async _createSortable() {
|
||||
if (!Sortable) {
|
||||
const sortableImport = await import(
|
||||
"sortablejs/modular/sortable.core.esm"
|
||||
);
|
||||
|
||||
Sortable = sortableImport.Sortable;
|
||||
Sortable.mount(sortableImport.OnSpill);
|
||||
Sortable.mount(sortableImport.AutoScroll());
|
||||
}
|
||||
|
||||
private _createSortable() {
|
||||
this._sortable = new Sortable(this.shadowRoot!.querySelector(".entities"), {
|
||||
animation: 150,
|
||||
fallbackClass: "sortable-fallback",
|
||||
|
||||
@@ -713,8 +713,7 @@ class HUIRoot extends LitElement {
|
||||
});
|
||||
}
|
||||
|
||||
private _moveViewLeft(ev) {
|
||||
ev.stopPropagation();
|
||||
private _moveViewLeft() {
|
||||
if (this._curView === 0) {
|
||||
return;
|
||||
}
|
||||
@@ -725,8 +724,7 @@ class HUIRoot extends LitElement {
|
||||
lovelace.saveConfig(swapView(lovelace.config, oldIndex, newIndex));
|
||||
}
|
||||
|
||||
private _moveViewRight(ev) {
|
||||
ev.stopPropagation();
|
||||
private _moveViewRight() {
|
||||
if ((this._curView! as number) + 1 === this.lovelace!.config.views.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -13,7 +13,6 @@ import "@formatjs/intl-relativetimeformat/polyfill";
|
||||
import "@formatjs/intl-relativetimeformat/locale-data/en";
|
||||
import "@formatjs/intl-datetimeformat/polyfill";
|
||||
import "@formatjs/intl-datetimeformat/locale-data/en";
|
||||
import "@formatjs/intl-datetimeformat/add-all-tz";
|
||||
|
||||
// To use comlink under ES5
|
||||
import "proxy-polyfill";
|
||||
|
||||
@@ -179,16 +179,7 @@ export const connectionMixin = <T extends Constructor<HassBaseEl>>(
|
||||
});
|
||||
|
||||
subscribeEntities(conn, (states) => this._updateHass({ states }));
|
||||
subscribeConfig(conn, (config) => {
|
||||
if (
|
||||
this.hass?.config?.time_zone !== config.time_zone &&
|
||||
"__setDefaultTimeZone" in Intl.DateTimeFormat
|
||||
) {
|
||||
// @ts-ignore
|
||||
Intl.DateTimeFormat.__setDefaultTimeZone(config.time_zone);
|
||||
}
|
||||
this._updateHass({ config });
|
||||
});
|
||||
subscribeConfig(conn, (config) => this._updateHass({ config }));
|
||||
subscribeServices(conn, (services) => this._updateHass({ services }));
|
||||
subscribePanels(conn, (panels) => this._updateHass({ panels }));
|
||||
subscribeFrontendUserData(conn, "core", (userData) =>
|
||||
|
||||
+26
-32
@@ -2831,10 +2831,8 @@
|
||||
"reinterview_device": "Re-interview Device",
|
||||
"heal_node": "Heal Device",
|
||||
"remove_failed": "Remove Failed Device",
|
||||
"highest_security": "Highest Security",
|
||||
"unknown": "Unknown",
|
||||
"zwave_plus": "Z-Wave Plus",
|
||||
"zwave_plus_version": "Version {version}"
|
||||
"is_secure": "Secure",
|
||||
"unknown": "Unknown"
|
||||
},
|
||||
"node_config": {
|
||||
"header": "Z-Wave Device Configuration",
|
||||
@@ -2870,27 +2868,24 @@
|
||||
"inclusion_finished": "The device has been added.",
|
||||
"view_device": "View Device",
|
||||
"interview_started": "The device is being interviewed. This may take some time.",
|
||||
"interview_failed": "The device interview failed. Additional information may be available in the logs."
|
||||
},
|
||||
"security_classes": {
|
||||
"None": {
|
||||
"title": "None"
|
||||
},
|
||||
"S2_Unauthenticated": {
|
||||
"title": "S2 Unauthenticated",
|
||||
"description": "Like S2 Authenticated, but without verification that the correct device is included"
|
||||
},
|
||||
"S2_Authenticated": {
|
||||
"title": "S2 Authenticated",
|
||||
"description": "Example: Lighting, Sensors and Security Systems"
|
||||
},
|
||||
"S2_AccessControl": {
|
||||
"title": "S2 Access Control",
|
||||
"description": "Example: Door Locks and Garage Doors"
|
||||
},
|
||||
"S0_Legacy": {
|
||||
"title": "S0 Legacy",
|
||||
"description": "Example: Legacy Door Locks without S2 support"
|
||||
"interview_failed": "The device interview failed. Additional information may be available in the logs.",
|
||||
"security_classes": {
|
||||
"S2_Unauthenticated": {
|
||||
"title": "S2 Unauthenticated",
|
||||
"description": "Like S2 Authenticated, but without verification that the correct device is included"
|
||||
},
|
||||
"S2_Authenticated": {
|
||||
"title": "S2 Authenticated",
|
||||
"description": "Example: Lighting, Sensors and Security Systems"
|
||||
},
|
||||
"S2_AccessControl": {
|
||||
"title": "S2 Access Control",
|
||||
"description": "Example: Door Locks and Garage Doors"
|
||||
},
|
||||
"S0_Legacy": {
|
||||
"title": "S0 Legacy",
|
||||
"description": "Example: Legacy Door Locks without S2 support"
|
||||
}
|
||||
}
|
||||
},
|
||||
"remove_node": {
|
||||
@@ -3282,7 +3277,8 @@
|
||||
},
|
||||
"entity": {
|
||||
"name": "Entity",
|
||||
"description": "The Entity card gives you a quick overview of your entity’s state."
|
||||
"description": "The Entity card gives you a quick overview of your entity’s state.",
|
||||
"show_trend": "Show Trend?"
|
||||
},
|
||||
"button": {
|
||||
"name": "Button",
|
||||
@@ -3420,7 +3416,8 @@
|
||||
"name": "Sensor",
|
||||
"show_more_detail": "Show more detail",
|
||||
"graph_type": "Graph Type",
|
||||
"description": "The Sensor card gives you a quick overview of your sensors state with an optional graph to visualize change over time."
|
||||
"description": "The Sensor card gives you a quick overview of your sensors state with an optional graph to visualize change over time.",
|
||||
"show_trend": "Show Trend?"
|
||||
},
|
||||
"shopping-list": {
|
||||
"name": "Shopping List",
|
||||
@@ -4084,10 +4081,11 @@
|
||||
"description": "This add-on is using Ingress to embed its interface securely into Home Assistant."
|
||||
},
|
||||
"label": {
|
||||
"core": "Core",
|
||||
"stage": "stage",
|
||||
"rating": "rating",
|
||||
"hardware": "hardware",
|
||||
"host": "host",
|
||||
"hass": "hass",
|
||||
"hassio": "hassio",
|
||||
"docker": "docker",
|
||||
"host_pid": "host pid",
|
||||
@@ -4095,10 +4093,6 @@
|
||||
"auth": "auth",
|
||||
"ingress": "ingress"
|
||||
},
|
||||
"stages": {
|
||||
"experimental": "Experimental",
|
||||
"deprecated": "Deprecated"
|
||||
},
|
||||
"role": {
|
||||
"manager": "manager",
|
||||
"default": "default",
|
||||
|
||||
@@ -31,7 +31,6 @@ const hassAttributeUtil = {
|
||||
"power",
|
||||
"presence",
|
||||
"problem",
|
||||
"running",
|
||||
"safety",
|
||||
"smoke",
|
||||
"sound",
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
import { assert } from "chai";
|
||||
import { slugify } from "../../../src/common/string/slugify";
|
||||
|
||||
describe("slugify", () => {
|
||||
// With default delimiter
|
||||
assert.strictEqual(slugify("abc"), "abc");
|
||||
assert.strictEqual(slugify("ABC"), "abc");
|
||||
assert.strictEqual(slugify("abc DEF"), "abc_def");
|
||||
assert.strictEqual(slugify("abc-DEF"), "abc_def");
|
||||
assert.strictEqual(slugify("abc_DEF"), "abc_def");
|
||||
assert.strictEqual(slugify("abc å DEF"), "abc_a_def");
|
||||
assert.strictEqual(slugify("abc:DEF"), "abc_def");
|
||||
assert.strictEqual(slugify("abc&DEF"), "abc_and_def");
|
||||
assert.strictEqual(slugify("abc^^DEF"), "abcdef");
|
||||
assert.strictEqual(slugify("abc DEF"), "abc_def");
|
||||
assert.strictEqual(slugify("_abc DEF"), "abc_def");
|
||||
assert.strictEqual(slugify("abc DEF_"), "abc_def");
|
||||
assert.strictEqual(slugify("abc-DEF ghi"), "abc_def_ghi");
|
||||
assert.strictEqual(slugify("abc-DEF-ghi"), "abc_def_ghi");
|
||||
assert.strictEqual(slugify("abc - DEF - ghi"), "abc_def_ghi");
|
||||
assert.strictEqual(slugify("abc---DEF---ghi"), "abc_def_ghi");
|
||||
assert.strictEqual(slugify("___abc___DEF___ghi___"), "abc_def_ghi");
|
||||
|
||||
// With custom delimiter
|
||||
assert.strictEqual(slugify("abc def", "-"), "abc-def");
|
||||
assert.strictEqual(slugify("abc-def", "-"), "abc-def");
|
||||
});
|
||||
@@ -2898,17 +2898,17 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@mdi/js@npm:6.4.95":
|
||||
version: 6.4.95
|
||||
resolution: "@mdi/js@npm:6.4.95"
|
||||
checksum: 76055821b0b611090f1fadff3cb93b0d2c05f7c52778a0e8966abe1c85d3aae8312c497e91882bb8cb02bf9235edda901da944099553afb2dbacafb11ed40b18
|
||||
"@mdi/js@npm:6.3.95":
|
||||
version: 6.3.95
|
||||
resolution: "@mdi/js@npm:6.3.95"
|
||||
checksum: e0ecdf2a2b1b46ead42266cc9dcc29c08495c8d3af53314bf28e0c2b637d528e9e1d16e523c1ab678b78c8930a7d29ee91a43f35f15a4ae5aa00244bc836da54
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@mdi/svg@npm:6.4.95":
|
||||
version: 6.4.95
|
||||
resolution: "@mdi/svg@npm:6.4.95"
|
||||
checksum: 118945d58484dfa686b3127fabbbbf8e20c20ce1989d158cd270e3da53e22fd30d3c285e3f5d0472f602a9842f2adb8321d853f85ff397ccf8bfefa846bd729e
|
||||
"@mdi/svg@npm:6.3.95":
|
||||
version: 6.3.95
|
||||
resolution: "@mdi/svg@npm:6.3.95"
|
||||
checksum: ed8d8042fbac21ca2011c799ba57767502cce820c15cd99d5dbacd89275d0350bd9c89557c7301e42986c85e80975c6a72d96f92c4efdf667ddb3d730904225b
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -9026,8 +9026,8 @@ fsevents@^1.2.7:
|
||||
"@material/mwc-tab-bar": 0.25.2
|
||||
"@material/mwc-textfield": 0.25.2
|
||||
"@material/top-app-bar": 14.0.0-canary.353ca7e9f.0
|
||||
"@mdi/js": 6.4.95
|
||||
"@mdi/svg": 6.4.95
|
||||
"@mdi/js": 6.3.95
|
||||
"@mdi/svg": 6.3.95
|
||||
"@open-wc/dev-server-hmr": ^0.0.2
|
||||
"@polymer/app-layout": ^3.1.0
|
||||
"@polymer/iron-flex-layout": ^3.0.1
|
||||
|
||||
Reference in New Issue
Block a user