Home Assistant Vacuum Card for Synthwave theme
May 2, 2020 ยท View on GitHub
import { LitElement, html } from 'https://unpkg.com/@polymer/lit-element@0.6.5/lit-element.js?module';
const styles = html`
`
class VacuumCard extends LitElement { static get properties() { return { hass: Object, config: Object, mapUrl: String } }
get entity() { return this.hass.states[this.config.entity] }
get map() { return this.hass.states[this.config.map] }
firstUpdated() { this.refresh() }
refresh() { const cleaning = this.entity.state !== 'docked'
const url = this.map.attributes.entity_picture + `&t=${(new Date()).getTime()}`
const img = new Image();
img.onload = () => {
this.mapUrl = url
this.requestUpdate();
setTimeout(() => this.refresh(), cleaning ? 1000 : 5000)
}
img.src = url
}
handleMore() { const e = new Event('hass-more-info', { bubbles: true, composed: true }) e.detail = { entityId: this.entity.entity_id } this.dispatchEvent(e); }
handleSpeed(e) { const fan_speed = e.target.getAttribute('value'); this.callService('set_fan_speed', { fan_speed }); }
callService(service, options = {}) { this.hass.callService('vacuum', service, { entity_id: this.config.entity, ...options }); }
renderSource() { const { attributes: { fan_speed: source, fan_speed_list: sources } } = this.entity;
const selected = sources.indexOf(source);
return html`
<paper-menu-button class='source-menu' slot='dropdown-trigger'
.horizontalAlign=${'right'} .verticalAlign=${'top'}
.verticalOffset=${40} .noAnimations=${true}
@click='${(e) => e.stopPropagation()}'>
<paper-button class='source-menu__button' slot='dropdown-trigger'>
<span class='source-menu__source' show=${true}>
${source}
</span>
<ha-icon icon="mdi:fan"></ha-icon>
</paper-button>
<paper-listbox slot='dropdown-content' selected=${selected}
@click='${(e) => this.handleSpeed(e)}'>
${sources.map(item => html`<paper-item value=${item}>${item}</paper-item>`)}
</paper-listbox>
</paper-menu-button>`;
}
renderVacuumStats() { const { attributes: { main_brush_left, side_brush_left, filter_left, sensor_dirty_left } } = this.entity
return html`
<div class="stats-block">
<span class="stats-hours">${filter_left}</span> <sup>hours</sup>
<div class="stats-subtitle">Filter</div>
</div>
<div class="stats-block">
<span class="stats-hours">${side_brush_left}</span> <sup>hours</sup>
<div class="stats-subtitle">Side brush</div>
</div>
<div class="stats-block">
<span class="stats-hours">${main_brush_left}</span> <sup>hours</sup>
<div class="stats-subtitle">Main brush</div>
</div>
<div class="stats-block">
<span class="stats-hours">${sensor_dirty_left}</span> <sup>hours</sup>
<div class="stats-subtitle">Sensors</div>
</div>
`
}
renderCleaningStats() { const { attributes: { cleaned_area, cleaning_time } } = this.entity
return html`
<div class="stats-block">
<span class="stats-hours">${cleaned_area}</span> m<sup>2</sup>
<div class="stats-subtitle">Cleaning area</div>
</div>
<div class="stats-block">
<span class="stats-hours">${cleaning_time}</span> minutes
<div class="stats-subtitle">Cleaning time</div>
</div>
`
}
renderCleaningToolbar() {
return html <div class="toolbar"> <paper-button @click='${(e) => this.callService('return_to_base')}'> <ha-icon icon="hass:home-map-marker" ></ha-icon> Return to dock </paper-button> </div>
}
renderDockedToolbar() {
const { actions } = this.config
const buttons = actions.map(({ name, service, icon }) => {
const execute = () => {
const args = service.split('.')
this.hass.callService(args[0], args[1]);
}
return html<paper-icon-button icon="${icon}" title="${name}" @click='${execute}'></paper-icon-button>
})
return html`
<div class="toolbar">
<paper-icon-button icon="hass:play"
title="Clean" class="toolbar-icon"
@click='${(e) => this.callService('start')}'>
</paper-icon-button>
<paper-icon-button icon="mdi:crosshairs-gps"
title="Locate vacuum" class="toolbar-split"
@click='${(e) => this.callService('locate')}'>
</paper-icon-button>
<div class="fill-gap"></div>
${buttons}
</div>
`
}
render() { const { state, attributes: { status, battery_level, battery_icon, } } = this.entity
const cleaning = state !== 'docked'
return html`
${styles}
<ha-card>
<div class="preview" @click='${(e) => this.handleMore()}' ?more-info=true>
<div class="header">
<div class="status">${status}</div>
<div>
${this.renderSource()}
</div>
<div class="battery">
${battery_level}% <ha-icon icon="${battery_icon}"></ha-icon>
</div>
</div>
<img class="vacuum" src="${this.mapUrl}" />
<div class="stats">
${cleaning ? this.renderCleaningStats() : this.renderVacuumStats()}
</div>
</div>
${cleaning ? this.renderCleaningToolbar() : this.renderDockedToolbar()}
</ha-card>
`
}
setConfig(config) { this.config = config; }
getCardSize() { return 2; } }
customElements.define('vacuum-card', VacuumCard);