Home-AssistantConfig/config/www/community/search-card/search-card.js

194 lines
4.9 KiB
JavaScript

customElements.whenDefined('card-tools').then(() => {
var ct = customElements.get('card-tools');
const BUILTIN_ACTIONS = [
{
matches: '^((magnet:.*)|(.*\.torrent.*))$',
name: 'Add to Transmission',
icon: 'mdi:progress-download',
service: 'transmission.add_torrent',
service_data: {
torrent: '{1}'
},
}
];
const matchAndReplace = (text, matches) => {
for (var i = 0; i < matches.length; i++) {
text = text.replace('{' + i + '}', matches[i]);
}
return text;
}
class SearchCard extends ct.LitElement {
static get properties() {
return {
config: {},
hass: {},
};
}
setConfig(config) {
this.results = [];
this.config = config;
this.active_actions = [];
this.max_results = this.config.max_results || 10;
this.actions = BUILTIN_ACTIONS.concat(this.config.actions || []);
}
getCardSize() {
return 4;
}
render() {
var results = this.results.slice(0, this.max_results).sort();
var rows = results.map((entity_id) => this._createResultRow(entity_id));
var actions = this.active_actions.map((x) => this._createActionRow(x[0], x[1]));
return ct.LitHtml `
<ha-card>
<div id="searchContainer">
<paper-input id="searchText"
@value-changed="${this._valueChanged}"
no-label-float
label="Type to search...">
<iron-icon icon="mdi:magnify"
slot="prefix"></iron-icon>
<paper-icon-button slot="suffix"
@click="${this._clearInput}"
icon="mdi:close"
alt="Clear"
title="Clear"></paper-icon-button>
</paper-input>
${results.length > 0 ?
ct.LitHtml `<div id="count">Showing ${results.length} of ${this.results.length} results</div>`
: ''}
</div>
${(rows.length > 0 || actions.length > 0) ?
ct.LitHtml `<div id="results">${actions}${rows}</div>`
: ''}
</ha-card>
`;
}
_createResultRow(entity_id) {
var row = ct.createEntityRow({entity: entity_id});
row.addEventListener("click", () => ct.moreInfo(entity_id));
row.hass = this.hass;
return row;
}
_createActionRow(action, matches) {
var service_data = action.service_data;
for (var key in service_data) {
service_data[key] = matchAndReplace(service_data[key], matches);
}
const elem = cardTools.createThing("service-row", {
type: "call",
name: matchAndReplace(action.name, matches),
icon: action.icon || 'mdi:lamp',
service: action.service,
service_data: service_data,
});
elem.hass = this.hass;
return elem;
}
_clearInput()
{
this.shadowRoot.getElementById('searchText').value = '';
super.update()
}
_valueChanged(ev) {
var searchText = ev.target.value;
this.results = [];
this.active_actions = [];
if (!this.config || !this.hass || searchText === "") {
this.update();
return;
}
try {
var searchRegex = new RegExp(searchText, 'i');
for (var entity_id in this.hass.states) {
if (
(entity_id.search(searchRegex) >= 0) ||
(
"friendly_name" in this.hass.states[entity_id].attributes &&
this.hass.states[entity_id].attributes.friendly_name.search(searchRegex) >= 0
)
) {
this.results.push(entity_id);
}
}
} catch (err) {
}
this.active_actions = this._getActivatedActions(searchText);
this.update();
}
_getActivatedActions(searchText) {
var active = [];
for (const action of this.actions) {
if (this._serviceExists(action.service)) {
var matches = searchText.match(action.matches);
if (matches != null) {
active.push([action, matches]);
}
}
}
return active;
}
_serviceExists(serviceCall) {
var [domain, service] = serviceCall.split('.');
var servicesForDomain = this.hass.services[domain];
return servicesForDomain && service in servicesForDomain;
}
static get styles() {
return ct.LitCSS `
#searchContainer {
width: 90%;
display: block;
margin-left: auto;
margin-right: auto;
}
#count {
text-align: right;
font-style: italic;
}
#results {
width: 90%;
display: block;
padding-bottom: 15px;
margin-top: 15px;
margin-left: auto;
margin-right: auto;
}
`;
}
}
customElements.define('search-card', SearchCard);
});
setTimeout(() => {
if(customElements.get('card-tools')) return;
customElements.define('search-card', class extends HTMLElement{
setConfig() { throw new Error("Can't find card-tools. See https://github.com/thomasloven/lovelace-card-tools");}
});
}, 2000);