mirror of
https://github.com/home-assistant/frontend.git
synced 2025-12-20 02:38:53 +00:00
375 lines
8.7 KiB
JavaScript
375 lines
8.7 KiB
JavaScript
import "@polymer/iron-flex-layout/iron-flex-layout-classes";
|
|
import { timeOut } from "@polymer/polymer/lib/utils/async";
|
|
import { Debouncer } from "@polymer/polymer/lib/utils/debounce";
|
|
import { html } from "@polymer/polymer/lib/utils/html-tag";
|
|
import { PolymerElement } from "@polymer/polymer/polymer-element";
|
|
|
|
import "../cards/ha-badges-card";
|
|
import "../cards/ha-card-chooser";
|
|
import "./ha-demo-badge";
|
|
|
|
import { computeStateDomain } from "../common/entity/compute_state_domain";
|
|
import { splitByGroups } from "../common/entity/split_by_groups";
|
|
import { getGroupEntities } from "../common/entity/get_group_entities";
|
|
|
|
// mapping domain to size of the card.
|
|
const DOMAINS_WITH_CARD = {
|
|
camera: 4,
|
|
history_graph: 4,
|
|
media_player: 3,
|
|
persistent_notification: 0,
|
|
plant: 3,
|
|
weather: 4,
|
|
};
|
|
|
|
// 4 types:
|
|
// badges: 0 .. 10
|
|
// before groups < 0
|
|
// groups: X
|
|
// rest: 100
|
|
|
|
const PRIORITY = {
|
|
// before groups < 0
|
|
configurator: -20,
|
|
persistent_notification: -15,
|
|
|
|
// badges have priority >= 0
|
|
updater: 0,
|
|
sun: 1,
|
|
person: 2,
|
|
device_tracker: 3,
|
|
alarm_control_panel: 4,
|
|
timer: 5,
|
|
sensor: 6,
|
|
binary_sensor: 7,
|
|
mailbox: 8,
|
|
};
|
|
|
|
const getPriority = (domain) => (domain in PRIORITY ? PRIORITY[domain] : 100);
|
|
|
|
const sortPriority = (domainA, domainB) => domainA.priority - domainB.priority;
|
|
|
|
const entitySortBy = (entityA, entityB) => {
|
|
const nameA = (
|
|
entityA.attributes.friendly_name || entityA.entity_id
|
|
).toLowerCase();
|
|
const nameB = (
|
|
entityB.attributes.friendly_name || entityB.entity_id
|
|
).toLowerCase();
|
|
|
|
if (nameA < nameB) {
|
|
return -1;
|
|
}
|
|
if (nameA > nameB) {
|
|
return 1;
|
|
}
|
|
return 0;
|
|
};
|
|
|
|
const iterateDomainSorted = (collection, func) => {
|
|
Object.keys(collection)
|
|
.map((key) => collection[key])
|
|
.sort(sortPriority)
|
|
.forEach((domain) => {
|
|
domain.states.sort(entitySortBy);
|
|
func(domain);
|
|
});
|
|
};
|
|
|
|
class HaCards extends PolymerElement {
|
|
static get template() {
|
|
return html`
|
|
<style include="iron-flex iron-flex-factors"></style>
|
|
<style>
|
|
:host {
|
|
display: block;
|
|
padding: 4px 4px 0;
|
|
transform: translateZ(0);
|
|
position: relative;
|
|
}
|
|
|
|
.badges {
|
|
font-size: 85%;
|
|
text-align: center;
|
|
padding-top: 16px;
|
|
}
|
|
|
|
.column {
|
|
max-width: 500px;
|
|
overflow-x: hidden;
|
|
}
|
|
|
|
ha-card-chooser {
|
|
display: block;
|
|
margin: 4px 4px 8px;
|
|
}
|
|
|
|
@media (max-width: 500px) {
|
|
:host {
|
|
padding-left: 0;
|
|
padding-right: 0;
|
|
}
|
|
|
|
ha-card-chooser {
|
|
margin-left: 0;
|
|
margin-right: 0;
|
|
}
|
|
}
|
|
|
|
@media (max-width: 599px) {
|
|
.column {
|
|
max-width: 600px;
|
|
}
|
|
}
|
|
</style>
|
|
|
|
<div id="main">
|
|
<template is="dom-if" if="[[cards.badges.length]]">
|
|
<div class="badges">
|
|
<template is="dom-if" if="[[cards.demo]]">
|
|
<ha-demo-badge></ha-demo-badge>
|
|
</template>
|
|
|
|
<ha-badges-card
|
|
states="[[cards.badges]]"
|
|
hass="[[hass]]"
|
|
></ha-badges-card>
|
|
</div>
|
|
</template>
|
|
|
|
<div class="horizontal layout center-justified">
|
|
<template is="dom-repeat" items="[[cards.columns]]" as="column">
|
|
<div class="column flex-1">
|
|
<template is="dom-repeat" items="[[column]]" as="card">
|
|
<ha-card-chooser card-data="[[card]]"></ha-card-chooser>
|
|
</template>
|
|
</div>
|
|
</template>
|
|
</div>
|
|
</div>
|
|
`;
|
|
}
|
|
|
|
static get properties() {
|
|
return {
|
|
hass: Object,
|
|
|
|
columns: {
|
|
type: Number,
|
|
value: 2,
|
|
},
|
|
|
|
states: Object,
|
|
|
|
viewVisible: {
|
|
type: Boolean,
|
|
value: false,
|
|
},
|
|
|
|
orderedGroupEntities: Array,
|
|
|
|
cards: Object,
|
|
};
|
|
}
|
|
|
|
static get observers() {
|
|
return ["updateCards(columns, states, viewVisible, orderedGroupEntities)"];
|
|
}
|
|
|
|
updateCards(columns, states, viewVisible, orderedGroupEntities) {
|
|
if (!viewVisible) {
|
|
if (this.$.main.parentNode) {
|
|
this.$.main._parentNode = this.$.main.parentNode;
|
|
this.$.main.parentNode.removeChild(this.$.main);
|
|
}
|
|
return;
|
|
}
|
|
if (!this.$.main.parentNode && this.$.main._parentNode) {
|
|
this.$.main._parentNode.appendChild(this.$.main);
|
|
}
|
|
this._debouncer = Debouncer.debounce(
|
|
this._debouncer,
|
|
timeOut.after(10),
|
|
() => {
|
|
// Things might have changed since it got scheduled.
|
|
if (this.viewVisible) {
|
|
this.cards = this.computeCards(columns, states, orderedGroupEntities);
|
|
}
|
|
}
|
|
);
|
|
}
|
|
|
|
emptyCards() {
|
|
return {
|
|
demo: false,
|
|
badges: [],
|
|
columns: [],
|
|
};
|
|
}
|
|
|
|
computeCards(columns, states, orderedGroupEntities) {
|
|
const hass = this.hass;
|
|
|
|
const cards = this.emptyCards();
|
|
|
|
const entityCount = [];
|
|
for (let i = 0; i < columns; i++) {
|
|
cards.columns.push([]);
|
|
entityCount.push(0);
|
|
}
|
|
|
|
// Find column with < 5 entities, else column with lowest count
|
|
function getIndex(size) {
|
|
let minIndex = 0;
|
|
for (let i = 0; i < entityCount.length; i++) {
|
|
if (entityCount[i] < 5) {
|
|
minIndex = i;
|
|
break;
|
|
}
|
|
if (entityCount[i] < entityCount[minIndex]) {
|
|
minIndex = i;
|
|
}
|
|
}
|
|
|
|
entityCount[minIndex] += size;
|
|
|
|
return minIndex;
|
|
}
|
|
|
|
function addEntitiesCard(name, entities, groupEntity) {
|
|
if (entities.length === 0) return;
|
|
|
|
const owncard = [];
|
|
const other = [];
|
|
|
|
let size = 0;
|
|
|
|
entities.forEach((entity) => {
|
|
const domain = computeStateDomain(entity);
|
|
|
|
if (
|
|
domain in DOMAINS_WITH_CARD &&
|
|
!entity.attributes.custom_ui_state_card
|
|
) {
|
|
owncard.push(entity);
|
|
size += DOMAINS_WITH_CARD[domain];
|
|
} else {
|
|
other.push(entity);
|
|
size++;
|
|
}
|
|
});
|
|
|
|
// Add 1 to the size if we're rendering entities card
|
|
size += other.length > 0;
|
|
|
|
const curIndex = getIndex(size);
|
|
|
|
if (other.length > 0) {
|
|
cards.columns[curIndex].push({
|
|
hass: hass,
|
|
cardType: "entities",
|
|
states: other,
|
|
groupEntity: groupEntity || false,
|
|
});
|
|
}
|
|
|
|
owncard.forEach((entity) => {
|
|
cards.columns[curIndex].push({
|
|
hass: hass,
|
|
cardType: computeStateDomain(entity),
|
|
stateObj: entity,
|
|
});
|
|
});
|
|
}
|
|
|
|
const splitted = splitByGroups(states);
|
|
if (orderedGroupEntities) {
|
|
splitted.groups.sort(
|
|
(gr1, gr2) =>
|
|
orderedGroupEntities[gr1.entity_id] -
|
|
orderedGroupEntities[gr2.entity_id]
|
|
);
|
|
} else {
|
|
splitted.groups.sort(
|
|
(gr1, gr2) => gr1.attributes.order - gr2.attributes.order
|
|
);
|
|
}
|
|
|
|
const badgesColl = {};
|
|
const beforeGroupColl = {};
|
|
const afterGroupedColl = {};
|
|
|
|
Object.keys(splitted.ungrouped).forEach((key) => {
|
|
const state = splitted.ungrouped[key];
|
|
const domain = computeStateDomain(state);
|
|
|
|
if (domain === "a") {
|
|
cards.demo = true;
|
|
return;
|
|
}
|
|
|
|
const priority = getPriority(domain);
|
|
let coll;
|
|
|
|
if (priority < 0) {
|
|
coll = beforeGroupColl;
|
|
} else if (priority < 10) {
|
|
coll = badgesColl;
|
|
} else {
|
|
coll = afterGroupedColl;
|
|
}
|
|
|
|
if (!(domain in coll)) {
|
|
coll[domain] = {
|
|
domain: domain,
|
|
priority: priority,
|
|
states: [],
|
|
};
|
|
}
|
|
|
|
coll[domain].states.push(state);
|
|
});
|
|
|
|
if (orderedGroupEntities) {
|
|
Object.keys(badgesColl)
|
|
.map((key) => badgesColl[key])
|
|
.forEach((domain) => {
|
|
cards.badges.push.apply(cards.badges, domain.states);
|
|
});
|
|
|
|
cards.badges.sort(
|
|
(e1, e2) =>
|
|
orderedGroupEntities[e1.entity_id] -
|
|
orderedGroupEntities[e2.entity_id]
|
|
);
|
|
} else {
|
|
iterateDomainSorted(badgesColl, (domain) => {
|
|
cards.badges.push.apply(cards.badges, domain.states);
|
|
});
|
|
}
|
|
|
|
iterateDomainSorted(beforeGroupColl, (domain) => {
|
|
addEntitiesCard(domain.domain, domain.states);
|
|
});
|
|
|
|
splitted.groups.forEach((groupState) => {
|
|
const entities = getGroupEntities(states, groupState);
|
|
addEntitiesCard(
|
|
groupState.entity_id,
|
|
Object.keys(entities).map((key) => entities[key]),
|
|
groupState
|
|
);
|
|
});
|
|
|
|
iterateDomainSorted(afterGroupedColl, (domain) => {
|
|
addEntitiesCard(domain.domain, domain.states);
|
|
});
|
|
|
|
// Remove empty columns
|
|
cards.columns = cards.columns.filter((val) => val.length > 0);
|
|
|
|
return cards;
|
|
}
|
|
}
|
|
customElements.define("ha-cards", HaCards);
|