Card Picker with Previews of cards (#4975)

* Card Picker with Previews of cards

* Getting Preview Async - Using dialogs entities

* Create generic getElement - filter entities before

* lint

* Add entities back to Picker. Set Qualifier

* Style Updates

* Move setup of filtered cards to connected

* style updates

* Dont pull entities if noEntity config

* Move all config logic to getConfig

* Style Update - Remove Manual process

* lint

* Accounting for ll-rebuild for async cards

* Style Updates - Use GetStubConfig for most

* Lint

* Filter entities with function - style - no preview

* Iframe rename and description

* Move getstubconfig to helper - update spinner

* Style for themes

* Move entities to be calc once

* Should update

* oops

* TSC

* Comments
This commit is contained in:
Zack Arnett 2020-03-03 14:53:55 -05:00 committed by GitHub
parent f3445d99cf
commit aa2e632df3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
24 changed files with 679 additions and 91 deletions

View File

@ -24,6 +24,8 @@ import {
import { AlarmPanelCardConfig } from "./types";
import { PaperInputElement } from "@polymer/paper-input/paper-input";
import { applyThemesOnElement } from "../../../common/dom/apply_themes_on_element";
import { findEntities } from "../common/find-entites";
import { LovelaceConfig } from "../../../data/lovelace";
const ICONS = {
armed_away: "hass:shield-lock",
@ -46,8 +48,27 @@ class HuiAlarmPanelCard extends LitElement implements LovelaceCard {
return document.createElement("hui-alarm-panel-card-editor");
}
public static getStubConfig() {
return { states: ["arm_home", "arm_away"], entity: "" };
public static getStubConfig(
hass: HomeAssistant,
lovelaceConfig: LovelaceConfig,
entities?: string[],
entitiesFill?: string[]
) {
const includeDomains = ["alarm_control_panel"];
const maxEntities = 1;
const foundEntities = findEntities(
hass,
lovelaceConfig,
maxEntities,
entities,
entitiesFill,
includeDomains
);
return {
states: ["arm_home", "arm_away"],
entity: foundEntities[0] || "",
};
}
@property() public hass?: HomeAssistant;

View File

@ -30,9 +30,10 @@ import { ButtonCardConfig } from "./types";
import { actionHandler } from "../common/directives/action-handler-directive";
import { hasAction } from "../common/has-action";
import { handleAction } from "../common/handle-action";
import { ActionHandlerEvent } from "../../../data/lovelace";
import { ActionHandlerEvent, LovelaceConfig } from "../../../data/lovelace";
import { computeActiveState } from "../../../common/entity/compute_active_state";
import { iconColorCSS } from "../../../common/style/icon_color_css";
import { findEntities } from "../common/find-entites";
@customElement("hui-button-card")
export class HuiButtonCard extends LitElement implements LovelaceCard {
@ -43,13 +44,28 @@ export class HuiButtonCard extends LitElement implements LovelaceCard {
return document.createElement("hui-button-card-editor");
}
public static getStubConfig(): object {
public static getStubConfig(
hass: HomeAssistant,
lovelaceConfig: LovelaceConfig,
entities?: string[],
entitiesFill?: string[]
): object {
const maxEntities = 1;
const foundEntities = findEntities(
hass,
lovelaceConfig,
maxEntities,
entities,
entitiesFill
);
return {
tap_action: { action: "toggle" },
hold_action: { action: "more-info" },
show_icon: true,
show_name: true,
state_color: true,
entity: foundEntities[0] || "",
};
}

View File

@ -27,6 +27,8 @@ import { createHeaderFooterElement } from "../create-element/create-header-foote
import { LovelaceHeaderFooterConfig } from "../header-footer/types";
import { DOMAINS_TOGGLE } from "../../../common/const";
import { computeDomain } from "../../../common/entity/compute_domain";
import { LovelaceConfig } from "../../../data/lovelace";
import { findEntities } from "../common/find-entites";
@customElement("hui-entities-card")
class HuiEntitiesCard extends LitElement implements LovelaceCard {
@ -37,8 +39,22 @@ class HuiEntitiesCard extends LitElement implements LovelaceCard {
return document.createElement("hui-entities-card-editor");
}
public static getStubConfig(): object {
return { entities: [] };
public static getStubConfig(
hass: HomeAssistant,
lovelaceConfig: LovelaceConfig,
entities?: string[],
entitiesFill?: string[]
) {
const maxEntities = 3;
const foundEntities = findEntities(
hass,
lovelaceConfig,
maxEntities,
entities,
entitiesFill
);
return { entities: foundEntities };
}
@property() private _config?: EntitiesCardConfig;

View File

@ -22,6 +22,9 @@ import { fireEvent } from "../../../common/dom/fire_event";
import { hasConfigOrEntityChanged } from "../common/has-changed";
import { LovelaceCard, LovelaceCardEditor } from "../types";
import { GaugeCardConfig } from "./types";
import { LovelaceConfig } from "../../../data/lovelace";
import { findEntities } from "../common/find-entites";
import { HassEntity } from "home-assistant-js-websocket/dist/types";
export const severityMap = {
red: "var(--label-badge-red)",
@ -38,8 +41,30 @@ class HuiGaugeCard extends LitElement implements LovelaceCard {
);
return document.createElement("hui-gauge-card-editor");
}
public static getStubConfig(): object {
return { entity: "" };
public static getStubConfig(
hass: HomeAssistant,
lovelaceConfig: LovelaceConfig,
entities?: string[],
entitiesFill?: string[]
): object {
const includeDomains = ["sensor"];
const maxEntities = 1;
const entityFilter = (stateObj: HassEntity): boolean => {
return !isNaN(Number(stateObj.state));
};
const foundEntities = findEntities(
hass,
lovelaceConfig,
maxEntities,
entities,
entitiesFill,
includeDomains,
entityFilter
);
return { entity: foundEntities[0] || "" };
}
@property() public hass?: HomeAssistant;

View File

@ -27,10 +27,11 @@ import { processConfigEntities } from "../common/process-config-entities";
import { GlanceCardConfig, GlanceConfigEntity } from "./types";
import { actionHandler } from "../common/directives/action-handler-directive";
import { hasAction } from "../common/has-action";
import { ActionHandlerEvent } from "../../../data/lovelace";
import { ActionHandlerEvent, LovelaceConfig } from "../../../data/lovelace";
import { handleAction } from "../common/handle-action";
import { computeDomain } from "../../../common/entity/compute_domain";
import { UNAVAILABLE, UNKNOWN } from "../../../data/entity";
import { findEntities } from "../common/find-entites";
@customElement("hui-glance-card")
export class HuiGlanceCard extends LitElement implements LovelaceCard {
@ -41,8 +42,24 @@ export class HuiGlanceCard extends LitElement implements LovelaceCard {
return document.createElement("hui-glance-card-editor");
}
public static getStubConfig(): object {
return { entities: [] };
public static getStubConfig(
hass: HomeAssistant,
lovelaceConfig: LovelaceConfig,
entities?: string[],
entitiesFill?: string[]
): object {
const includeDomains = ["sensor"];
const maxEntities = 3;
const foundEntities = findEntities(
hass,
lovelaceConfig,
maxEntities,
entities,
entitiesFill,
includeDomains
);
return { entities: foundEntities };
}
@property() public hass?: HomeAssistant;

View File

@ -20,6 +20,8 @@ import { HomeAssistant } from "../../../types";
import { HistoryGraphCardConfig } from "./types";
import { LovelaceCard } from "../types";
import { EntityConfig } from "../entity-rows/types";
import { LovelaceConfig } from "../../../data/lovelace";
import { findEntities } from "../common/find-entites";
@customElement("hui-history-graph-card")
export class HuiHistoryGraphCard extends LitElement implements LovelaceCard {
@ -30,8 +32,24 @@ export class HuiHistoryGraphCard extends LitElement implements LovelaceCard {
return document.createElement("hui-history-graph-card-editor");
}
public static getStubConfig() {
return { entities: [] };
public static getStubConfig(
hass: HomeAssistant,
lovelaceConfig: LovelaceConfig,
entities?: string[],
entitiesFill?: string[]
): object {
const includeDomains = ["sensor"];
const maxEntities = 1;
const foundEntities = findEntities(
hass,
lovelaceConfig,
maxEntities,
entities,
entitiesFill,
includeDomains
);
return { entities: foundEntities };
}
@property() public hass?: HomeAssistant;

View File

@ -29,6 +29,8 @@ import { toggleEntity } from "../common/entity/toggle-entity";
import { LightCardConfig } from "./types";
import { supportsFeature } from "../../../common/entity/supports-feature";
import { SUPPORT_BRIGHTNESS } from "../../../data/light";
import { LovelaceConfig } from "../../../data/lovelace";
import { findEntities } from "../common/find-entites";
import { UNAVAILABLE } from "../../../data/entity";
@customElement("hui-light-card")
@ -39,8 +41,25 @@ export class HuiLightCard extends LitElement implements LovelaceCard {
);
return document.createElement("hui-light-card-editor");
}
public static getStubConfig(): object {
return { entity: "" };
public static getStubConfig(
hass: HomeAssistant,
lovelaceConfig: LovelaceConfig,
entities?: string[],
entitiesFill?: string[]
): object {
const includeDomains = ["light"];
const maxEntities = 1;
const foundEntities = findEntities(
hass,
lovelaceConfig,
maxEntities,
entities,
entitiesFill,
includeDomains
);
return { entity: foundEntities[0] || "" };
}
@property() public hass?: HomeAssistant;

View File

@ -30,6 +30,8 @@ import { EntityConfig } from "../entity-rows/types";
import { processConfigEntities } from "../common/process-config-entities";
import { MapCardConfig } from "./types";
import { classMap } from "lit-html/directives/class-map";
import { LovelaceConfig } from "../../../data/lovelace";
import { findEntities } from "../common/find-entites";
@customElement("hui-map-card")
class HuiMapCard extends LitElement implements LovelaceCard {
@ -40,8 +42,24 @@ class HuiMapCard extends LitElement implements LovelaceCard {
return document.createElement("hui-map-card-editor");
}
public static getStubConfig() {
return { entities: [] };
public static getStubConfig(
hass: HomeAssistant,
lovelaceConfig: LovelaceConfig,
entities?: string[],
entitiesFill?: string[]
): object {
const includeDomains = ["device_tracker"];
const maxEntities = 2;
const foundEntities = findEntities(
hass,
lovelaceConfig,
maxEntities,
entities,
entitiesFill,
includeDomains
);
return { entities: foundEntities };
}
@property() public hass?: HomeAssistant;

View File

@ -34,6 +34,8 @@ import { LovelaceCard, LovelaceCardEditor } from "../types";
import { fireEvent } from "../../../common/dom/fire_event";
import { MediaControlCardConfig } from "./types";
import { UNAVAILABLE } from "../../../data/entity";
import { LovelaceConfig } from "../../../data/lovelace";
import { findEntities } from "../common/find-entites";
@customElement("hui-media-control-card")
export class HuiMediaControlCard extends LitElement implements LovelaceCard {
@ -44,8 +46,24 @@ export class HuiMediaControlCard extends LitElement implements LovelaceCard {
return document.createElement("hui-media-control-card-editor");
}
public static getStubConfig(): object {
return { entity: "" };
public static getStubConfig(
hass: HomeAssistant,
lovelaceConfig: LovelaceConfig,
entities?: string[],
entitiesFill?: string[]
): object {
const includeDomains = ["media_player"];
const maxEntities = 1;
const foundEntities = findEntities(
hass,
lovelaceConfig,
maxEntities,
entities,
entitiesFill,
includeDomains
);
return { entity: foundEntities[0] || "" };
}
@property() public hass?: HomeAssistant;

View File

@ -15,9 +15,39 @@ import { HomeAssistant } from "../../../types";
import { LovelaceElementConfig, LovelaceElement } from "../elements/types";
import { PictureElementsCardConfig } from "./types";
import { applyThemesOnElement } from "../../../common/dom/apply_themes_on_element";
import { LovelaceConfig } from "../../../data/lovelace";
import { findEntities } from "../common/find-entites";
@customElement("hui-picture-elements-card")
class HuiPictureElementsCard extends LitElement implements LovelaceCard {
public static getStubConfig(
hass: HomeAssistant,
lovelaceConfig: LovelaceConfig,
entities?: string[],
entitiesFill?: string[]
): object {
const maxEntities = 1;
const foundEntities = findEntities(
hass,
lovelaceConfig,
maxEntities,
entities,
entitiesFill
);
return {
elements: [
{
type: "state-badge",
entity: foundEntities[0] || "",
style: "position: absolute, transform: translate(-50%, -50%)",
},
],
image:
"https://www.home-assistant.io/images/merchandise/shirt-frontpage.png",
};
}
@property() private _config?: PictureElementsCardConfig;
private _hass?: HomeAssistant;

View File

@ -27,8 +27,9 @@ import { PictureEntityCardConfig } from "./types";
import { applyThemesOnElement } from "../../../common/dom/apply_themes_on_element";
import { actionHandler } from "../common/directives/action-handler-directive";
import { hasAction } from "../common/has-action";
import { ActionHandlerEvent } from "../../../data/lovelace";
import { ActionHandlerEvent, LovelaceConfig } from "../../../data/lovelace";
import { handleAction } from "../common/handle-action";
import { findEntities } from "../common/find-entites";
@customElement("hui-picture-entity-card")
class HuiPictureEntityCard extends LitElement implements LovelaceCard {
@ -38,9 +39,24 @@ class HuiPictureEntityCard extends LitElement implements LovelaceCard {
);
return document.createElement("hui-picture-entity-card-editor");
}
public static getStubConfig(): object {
public static getStubConfig(
hass: HomeAssistant,
lovelaceConfig: LovelaceConfig,
entities?: string[],
entitiesFill?: string[]
): object {
const maxEntities = 1;
const foundEntities = findEntities(
hass,
lovelaceConfig,
maxEntities,
entities,
entitiesFill
);
return {
entity: "",
entity: foundEntities[0] || "",
image:
"https://www.home-assistant.io/images/merchandise/shirt-frontpage.png",
};

View File

@ -29,8 +29,9 @@ import { hasConfigOrEntityChanged } from "../common/has-changed";
import { applyThemesOnElement } from "../../../common/dom/apply_themes_on_element";
import { actionHandler } from "../common/directives/action-handler-directive";
import { hasAction } from "../common/has-action";
import { ActionHandlerEvent } from "../../../data/lovelace";
import { ActionHandlerEvent, LovelaceConfig } from "../../../data/lovelace";
import { handleAction } from "../common/handle-action";
import { findEntities } from "../common/find-entites";
const STATES_OFF = new Set(["closed", "locked", "not_home", "off"]);
@ -42,11 +43,26 @@ class HuiPictureGlanceCard extends LitElement implements LovelaceCard {
);
return document.createElement("hui-picture-glance-card-editor");
}
public static getStubConfig(): object {
public static getStubConfig(
hass: HomeAssistant,
lovelaceConfig: LovelaceConfig,
entities?: string[],
entitiesFill?: string[]
): object {
const maxEntities = 2;
const foundEntities = findEntities(
hass,
lovelaceConfig,
maxEntities,
entities,
entitiesFill
);
return {
image:
"https://www.home-assistant.io/images/merchandise/shirt-frontpage.png",
entities: [],
entities: foundEntities,
};
}

View File

@ -22,6 +22,8 @@ import { hasConfigOrEntityChanged } from "../common/has-changed";
import { PlantStatusCardConfig, PlantAttributeTarget } from "./types";
import { applyThemesOnElement } from "../../../common/dom/apply_themes_on_element";
import { actionHandler } from "../common/directives/action-handler-directive";
import { LovelaceConfig } from "../../../data/lovelace";
import { findEntities } from "../common/find-entites";
const SENSORS = {
moisture: "hass:water",
@ -40,8 +42,24 @@ class HuiPlantStatusCard extends LitElement implements LovelaceCard {
return document.createElement("hui-plant-status-card-editor");
}
public static getStubConfig(): object {
return { entity: "" };
public static getStubConfig(
hass: HomeAssistant,
lovelaceConfig: LovelaceConfig,
entities?: string[],
entitiesFill?: string[]
): object {
const includeDomains = ["plant"];
const maxEntities = 1;
const foundEntities = findEntities(
hass,
lovelaceConfig,
maxEntities,
entities,
entitiesFill,
includeDomains
);
return { entity: foundEntities[0] || "" };
}
@property() public hass?: HomeAssistant;

View File

@ -26,6 +26,9 @@ import { fetchRecent } from "../../../data/history";
import { SensorCardConfig } from "./types";
import { hasConfigOrEntityChanged } from "../common/has-changed";
import { actionHandler } from "../common/directives/action-handler-directive";
import { LovelaceConfig } from "../../../data/lovelace";
import { findEntities } from "../common/find-entites";
import { HassEntity } from "home-assistant-js-websocket/dist/types";
const strokeWidth = 5;
@ -174,8 +177,32 @@ class HuiSensorCard extends LitElement implements LovelaceCard {
return document.createElement("hui-sensor-card-editor");
}
public static getStubConfig(): object {
return { entity: "" };
public static getStubConfig(
hass: HomeAssistant,
lovelaceConfig: LovelaceConfig,
entities?: string[],
entitiesFill?: string[]
): object {
const includeDomains = ["sensor"];
const maxEntities = 1;
const entityFilter = (stateObj: HassEntity): boolean => {
return (
!isNaN(Number(stateObj.state)) &&
!!stateObj.attributes.unit_of_measurement
);
};
const foundEntities = findEntities(
hass,
lovelaceConfig,
maxEntities,
entities,
entitiesFill,
includeDomains,
entityFilter
);
return { entity: foundEntities[0] || "", graph: "line" };
}
@property() public hass?: HomeAssistant;

View File

@ -34,6 +34,8 @@ import {
} from "../../../data/climate";
import { HassEntity } from "home-assistant-js-websocket";
import { actionHandler } from "../common/directives/action-handler-directive";
import { LovelaceConfig } from "../../../data/lovelace";
import { findEntities } from "../common/find-entites";
import { UNAVAILABLE } from "../../../data/entity";
const modeIcons: { [mode in HvacMode]: string } = {
@ -55,8 +57,24 @@ export class HuiThermostatCard extends LitElement implements LovelaceCard {
return document.createElement("hui-thermostat-card-editor");
}
public static getStubConfig(): object {
return { entity: "" };
public static getStubConfig(
hass: HomeAssistant,
lovelaceConfig: LovelaceConfig,
entities?: string[],
entitiesFill?: string[]
): object {
const includeDomains = ["climate"];
const maxEntities = 1;
const foundEntities = findEntities(
hass,
lovelaceConfig,
maxEntities,
entities,
entitiesFill,
includeDomains
);
return { entity: foundEntities[0] || "" };
}
@property() public hass?: HomeAssistant;

View File

@ -24,6 +24,8 @@ import { fireEvent } from "../../../common/dom/fire_event";
import { toggleAttribute } from "../../../common/dom/toggle_attribute";
import { applyThemesOnElement } from "../../../common/dom/apply_themes_on_element";
import { actionHandler } from "../common/directives/action-handler-directive";
import { LovelaceConfig } from "../../../data/lovelace";
import { findEntities } from "../common/find-entites";
const cardinalDirections = [
"N",
@ -71,8 +73,25 @@ class HuiWeatherForecastCard extends LitElement implements LovelaceCard {
);
return document.createElement("hui-weather-forecast-card-editor");
}
public static getStubConfig(): object {
return { entity: "" };
public static getStubConfig(
hass: HomeAssistant,
lovelaceConfig: LovelaceConfig,
entities?: string[],
entitiesFill?: string[]
): object {
const includeDomains = ["weather"];
const maxEntities = 1;
const foundEntities = findEntities(
hass,
lovelaceConfig,
maxEntities,
entities,
entitiesFill,
includeDomains
);
return { entity: foundEntities[0] || "" };
}
@property() public hass?: HomeAssistant;

View File

@ -1,7 +1,7 @@
import { LovelaceConfig, ActionConfig } from "../../../data/lovelace";
import { HomeAssistant } from "../../../types";
const EXCLUDED_DOMAINS = ["zone", "persistent_notification"];
export const EXCLUDED_DOMAINS = ["zone", "persistent_notification"];
const addFromAction = (entities: Set<string>, actionConfig: ActionConfig) => {
if (
@ -61,7 +61,7 @@ const addEntities = (entities: Set<string>, obj) => {
}
};
const computeUsedEntities = (config) => {
export const computeUsedEntities = (config) => {
const entities = new Set<string>();
config.views.forEach((view) => addEntities(entities, view));
return entities;

View File

@ -0,0 +1,59 @@
import {
computeUnusedEntities,
computeUsedEntities,
} from "./compute-unused-entities";
import { HomeAssistant } from "../../../types";
import { LovelaceConfig } from "../../../data/lovelace";
import { computeDomain } from "../../../common/entity/compute_domain";
import { HassEntity } from "home-assistant-js-websocket";
export const findEntities = (
hass: HomeAssistant,
lovelaceConfig: LovelaceConfig,
maxEntities: number,
entities?: string[],
entitiesFill?: string[],
includeDomains?: string[],
entityFilter?: (stateObj: HassEntity) => boolean
) => {
let entityIds: string[];
entityIds = !entities?.length
? computeUnusedEntities(hass, lovelaceConfig)
: entities;
if (includeDomains && includeDomains.length) {
entityIds = entityIds.filter((eid) =>
includeDomains!.includes(computeDomain(eid))
);
}
if (entityFilter) {
entityIds = entityIds.filter(
(eid) => hass.states[eid] && entityFilter(hass.states[eid])
);
}
if (entityIds.length < (maxEntities || 1)) {
let fillEntityIds =
entitiesFill && entitiesFill.length
? entitiesFill
: [...computeUsedEntities(lovelaceConfig)];
if (includeDomains && includeDomains.length) {
fillEntityIds = fillEntityIds.filter((eid) =>
includeDomains!.includes(computeDomain(eid))
);
}
if (entityFilter) {
fillEntityIds = fillEntityIds.filter(
(eid) => hass.states[eid] && entityFilter(hass.states[eid])
);
}
entityIds = [...entityIds, ...fillEntityIds];
}
return entityIds.slice(0, maxEntities);
};

View File

@ -5,26 +5,31 @@ import {
TemplateResult,
CSSResult,
customElement,
property,
PropertyValues,
} from "lit-element";
import "@material/mwc-button";
import { until } from "lit-html/directives/until";
import { classMap } from "lit-html/directives/class-map";
import { HomeAssistant } from "../../../../types";
import { LovelaceCardConfig } from "../../../../data/lovelace";
import { CardPickTarget } from "../types";
import { HomeAssistant } from "../../../../types";
import { LovelaceCard } from "../../types";
import { LovelaceCardConfig, LovelaceConfig } from "../../../../data/lovelace";
import { fireEvent } from "../../../../common/dom/fire_event";
import { getCardElementClass } from "../../create-element/create-card-element";
import { createCardElement } from "../../create-element/create-card-element";
import { getCardStubConfig } from "../get-card-stub-config";
import {
computeUnusedEntities,
computeUsedEntities,
} from "../../common/compute-unused-entities";
const cards: string[] = [
const previewCards: string[] = [
"alarm-panel",
"conditional",
"entities",
"button",
"entity-filter",
"gauge",
"glance",
"history-graph",
"horizontal-stack",
"iframe",
"light",
"map",
"markdown",
@ -35,78 +40,247 @@ const cards: string[] = [
"picture-glance",
"plant-status",
"sensor",
"shopping-list",
"thermostat",
"vertical-stack",
"weather-forecast",
];
const nonPreviewCards: string[] = [
"conditional",
"entity-filter",
"horizontal-stack",
"iframe",
"vertical-stack",
"shopping-list",
];
@customElement("hui-card-picker")
export class HuiCardPicker extends LitElement {
public hass?: HomeAssistant;
@property() public hass?: HomeAssistant;
public lovelace?: LovelaceConfig;
public cardPicked?: (cardConf: LovelaceCardConfig) => void;
private _unusedEntities?: string[];
private _usedEntities?: string[];
protected render(): TemplateResult {
if (
!this.hass ||
!this.lovelace ||
!this._unusedEntities ||
!this._usedEntities
) {
return html``;
}
return html`
<div class="cards-container">
${cards.map((card: string) => {
${previewCards.map((type: string) => {
return html`
<mwc-button @click="${this._cardPicked}" .type="${card}">
${this.hass!.localize(
`ui.panel.lovelace.editor.card.${card}.name`
)}
</mwc-button>
${until(
this._renderCardElement(type),
html`
<div class="card spinner">
<paper-spinner active alt="Loading"></paper-spinner>
</div>
`
)}
`;
})}
${nonPreviewCards.map((type: string) => {
return html`
${until(
this._renderCardElement(type, true),
html`
<div class="card spinner">
<paper-spinner active alt="Loading"></paper-spinner>
</div>
`
)}
`;
})}
</div>
<div class="cards-container">
<mwc-button @click="${this._manualPicked}">MANUAL CARD</mwc-button>
<div
class="card"
@click="${this._cardPicked}"
.config="${{ type: "" }}"
>
<div class="preview description">
${this.hass!.localize(
`ui.panel.lovelace.editor.card.generic.manual_description`
)}
</div>
<div class="card-header">
${this.hass!.localize(
`ui.panel.lovelace.editor.card.generic.manual`
)}
</div>
</div>
</div>
`;
}
protected shouldUpdate(changedProps: PropertyValues): boolean {
const oldHass = changedProps.get("hass") as HomeAssistant | undefined;
if (!oldHass) {
return true;
}
if (oldHass.language !== this.hass!.language) {
return true;
}
return false;
}
protected firstUpdated(): void {
if (!this.hass || !this.lovelace) {
return;
}
this._unusedEntities = computeUnusedEntities(this.hass, this.lovelace);
this._usedEntities = [...computeUsedEntities(this.lovelace)];
this.requestUpdate();
}
static get styles(): CSSResult[] {
return [
css`
.cards-container {
display: flex;
flex-wrap: wrap;
margin-bottom: 10px;
}
.cards-container mwc-button {
flex: 1 0 25%;
margin: 4px;
display: grid;
grid-gap: 8px 8px;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
margin-top: 20px;
}
@media all and (max-width: 450px), all and (max-height: 500px) {
.cards-container mwc-button {
flex: 1 0 33%;
}
.card {
height: 100%;
display: flex;
flex-direction: column;
border-radius: 4px;
border: 1px solid var(--divider-color);
background: var(--primary-background-color, #fafafa);
cursor: pointer;
box-sizing: border-box;
}
.card-header {
color: var(--ha-card-header-color, --primary-text-color);
font-family: var(--ha-card-header-font-family, inherit);
font-size: 16px;
letter-spacing: -0.012em;
line-height: 20px;
padding: 12px 16px;
display: block;
text-align: center;
background: var(
--ha-card-background,
var(--paper-card-background-color, white)
);
border-radius: 0 0 4px 4px;
border-top: 1px solid var(--divider-color);
}
.preview {
pointer-events: none;
margin: 20px;
flex: 1 1 0;
display: flex;
align-items: center;
justify-content: center;
}
.preview > :first-child {
zoom: 0.6;
-moz-transform: scale(0.6); /* Firefox */
-moz-transform-origin: 0 0;
-o-transform: scale(0.6); /* Opera */
-o-transform-origin: 0 0;
display: block;
width: 100%;
}
.description {
text-align: center;
}
.spinner {
align-items: center;
justify-content: center;
}
`,
];
}
private _manualPicked(): void {
fireEvent(this, "config-changed", {
config: { type: "" },
});
}
private async _cardPicked(ev: Event): Promise<void> {
const type = (ev.currentTarget! as CardPickTarget).type;
const elClass = await getCardElementClass(type);
let config: LovelaceCardConfig = { type };
if (elClass && elClass.getStubConfig) {
const cardConfig = elClass.getStubConfig(this.hass!);
config = { ...config, ...cardConfig };
}
private _cardPicked(ev: Event): void {
const config: LovelaceCardConfig = (ev.currentTarget! as CardPickTarget)
.config;
fireEvent(this, "config-changed", { config });
}
private _createCardElement(cardConfig: LovelaceCardConfig) {
const element = createCardElement(cardConfig) as LovelaceCard;
element.hass = this.hass;
element.addEventListener(
"ll-rebuild",
(ev) => {
ev.stopPropagation();
element.parentElement!.replaceChild(
this._createCardElement(cardConfig),
element
);
},
{ once: true }
);
return element;
}
private async _renderCardElement(
type: string,
noElement: boolean = false
): Promise<TemplateResult> {
let element: LovelaceCard | undefined;
let cardConfig: LovelaceCardConfig = { type };
if (this.hass && this.lovelace) {
cardConfig = await getCardStubConfig(
this.hass,
this.lovelace,
type,
this._unusedEntities,
this._usedEntities
);
if (!noElement) {
element = this._createCardElement(cardConfig);
}
}
return html`
<div class="card" @click="${this._cardPicked}" .config="${cardConfig}">
<div
class="preview ${classMap({
description: !element || element.tagName === "HUI-ERROR-CARD",
})}"
>
${!element || element.tagName === "HUI-ERROR-CARD"
? html`
${this.hass!.localize(
`ui.panel.lovelace.editor.card.${cardConfig.type}.description`
)}
`
: html`
${element}
`}
</div>
<div class="card-header">
${this.hass!.localize(
`ui.panel.lovelace.editor.card.${cardConfig.type}.name`
)}
</div>
</div>
`;
}
}
declare global {

View File

@ -99,6 +99,7 @@ export class HuiDialogEditCard extends LitElement {
${this._cardConfig === undefined
? html`
<hui-card-picker
.lovelace="${this._params.lovelaceConfig}"
.hass=${this.hass}
@config-changed="${this._handleCardPicked}"
></hui-card-picker>
@ -254,7 +255,7 @@ export class HuiDialogEditCard extends LitElement {
private _handleCardPicked(ev) {
const config = ev.detail.config;
if (this._params!.entities && this._params!.entities.length > 0) {
if (this._params!.entities && this._params!.entities.length) {
if (Object.keys(config).includes("entities")) {
config.entities = this._params!.entities;
} else if (Object.keys(config).includes("entity")) {

View File

@ -0,0 +1,28 @@
import { HomeAssistant } from "../../../types";
import { LovelaceCardConfig, LovelaceConfig } from "../../../data/lovelace";
import { getCardElementClass } from "../create-element/create-card-element";
export const getCardStubConfig = async (
hass: HomeAssistant,
lovelaceConfig: LovelaceConfig,
type: string,
entities?: string[],
entitiesFill?: string[]
): Promise<LovelaceCardConfig> => {
let cardConfig: LovelaceCardConfig = { type };
const elClass = await getCardElementClass(type);
if (elClass && elClass.getStubConfig) {
const classStubConfig = elClass.getStubConfig(
hass,
lovelaceConfig,
entities,
entitiesFill
);
cardConfig = { ...cardConfig, ...classStubConfig };
}
return cardConfig;
};

View File

@ -46,7 +46,7 @@ export interface EditorTarget extends EventTarget {
}
export interface CardPickTarget extends EventTarget {
type: string;
config: LovelaceCardConfig;
}
export const actionConfigStruct = struct({

View File

@ -38,7 +38,12 @@ export interface LovelaceCard extends HTMLElement {
}
export interface LovelaceCardConstructor extends Constructor<LovelaceCard> {
getStubConfig?: (hass: HomeAssistant) => LovelaceCardConfig;
getStubConfig?: (
hass: HomeAssistant,
lovelaceConfig: LovelaceConfig,
entities?: string[],
entitiesFill?: string[]
) => LovelaceCardConfig;
getConfigElement?: () => LovelaceCardEditor;
}

View File

@ -1956,7 +1956,8 @@
"available_states": "Available States"
},
"conditional": {
"name": "Conditional"
"name": "Conditional",
"description": "Displays another card based on entity states."
},
"config": {
"required": "Required",
@ -1971,7 +1972,8 @@
"name": "Button"
},
"entity-filter": {
"name": "Entity Filter"
"name": "Entity Filter",
"description": "This card allows you to define a list of entities that you want to track only when in a certain state."
},
"gauge": {
"name": "Gauge",
@ -1990,10 +1992,12 @@
"name": "History Graph"
},
"horizontal-stack": {
"name": "Horizontal Stack"
"name": "Horizontal Stack",
"description": "Horizontal stack card allows you to stack together multiple cards, so they always sit next to each other in the space of one column."
},
"iframe": {
"name": "iFrame"
"name": "Webpage",
"description": "Embed your favorite webpage right into Home Assistant"
},
"light": {
"name": "Light"
@ -2010,6 +2014,8 @@
"icon_height": "Icon Height",
"image": "Image Path",
"maximum": "Maximum",
"manual": "Manual",
"manual_description": "Need to add a custom card or just want to manually write the yaml?",
"minimum": "Minimum",
"name": "Name",
"refresh_interval": "Refresh Interval",
@ -2049,7 +2055,8 @@
"name": "Picture Glance"
},
"plant-status": {
"name": "Plant Status"
"name": "Plant Status",
"description": "A card for all the lovely botanists out there."
},
"sensor": {
"name": "Sensor",
@ -2058,13 +2065,15 @@
},
"shopping-list": {
"name": "Shopping List",
"description": "The Shopping List Card allows you to add, edit, check-off, and clear items from your shopping list.",
"integration_not_loaded": "This card requires the `shopping_list` integration to be set up."
},
"thermostat": {
"name": "Thermostat"
},
"vertical-stack": {
"name": "Vertical Stack"
"name": "Vertical Stack",
"description": "Vertical stack allows you to group multiple cards so they always sit in the same column."
},
"weather-forecast": {
"name": "Weather Forecast"