Files
homeassistant/www/community/lovelace-xiaomi-vacuum-card/xiaomi-vacuum-card.js
2019-12-09 12:40:02 +00:00

275 lines
9.9 KiB
JavaScript

class XiaomiVacuumCard extends Polymer.Element {
static get template() {
return Polymer.html`
<style>
.background {
background-repeat: no-repeat;
background-position: center center;
background-size: cover;
}
.title {
font-size: 20px;
padding: 16px 16px 0;
text-align: center;
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
}
.content {
cursor: pointer;
}
.flex {
display: flex;
align-items: center;
justify-content: space-evenly;
}
.button {
cursor: pointer;
padding: 16px;
}
.grid {
display: grid;
grid-template-columns: repeat(2, auto);
}
.grid-content {
display: grid;
align-content: space-between;
grid-row-gap: 6px;
}
.grid-left {
text-align: left;
font-size: 110%;
padding-left: 10px;
border-left: 2px solid var(--primary-color);
}
.grid-right {
text-align: right;
padding-right: 10px;
border-right: 2px solid var(--primary-color);
}
</style>
<ha-card hass="[[_hass]]" config="[[_config]]" class="background" style="[[backgroundImage]]">
<template is="dom-if" if="{{name}}">
<div class="title" style="[[contentText]]">[[name]]</div>
</template>
<div class="content grid" style="[[contentStyle]]" on-click="moreInfo">
<div class="grid-content grid-left">
<div>[[getValue('status')]]</div>
<div>[[getValue('battery', ' %')]]</div>
<div>[[getValue('mode')]]</div>
</div>
<template is="dom-if" if="{{showDetails}}">
<div class="grid-content grid-right" >
<div>[[computeValue('main_brush')]]</div>
<div>[[computeValue('side_brush')]]</div>
<div>[[computeValue('filter')]]</div>
<div>[[computeValue('sensor')]]</div>
</div>
</template>
</div>
<template is="dom-if" if="{{showButtons}}">
<div class="flex" style="[[contentText]]">
<template is="dom-if" if="{{_config.buttons.start}}">
<div class="button" on-tap="startVaccum">
<ha-icon icon="mdi:play"></ha-icon>
</div>
</template>
<template is="dom-if" if="{{_config.buttons.pause}}">
<div class="button" on-tap="pauseVacuum">
<ha-icon icon="mdi:pause"></ha-icon>
</div>
</template>
<template is="dom-if" if="{{_config.buttons.stop}}">
<div class="button" on-tap="stopVacuum">
<ha-icon icon="mdi:stop"></ha-icon>
</div>
</template>
<template is="dom-if" if="{{_config.buttons.spot}}">
<div class="button" on-tap="cleanSpot">
<ha-icon icon="mdi:broom"></ha-icon>
</div>
</template>
<template is="dom-if" if="{{_config.buttons.locate}}">
<div class="button" on-tap="locateVacuum">
<ha-icon icon="mdi:map-marker"></ha-icon>
</div>
</template>
<template is="dom-if" if="{{_config.buttons.return}}">
<div class="button" on-tap="returnVacuum">
<ha-icon icon="mdi:home-map-marker"></ha-icon>
</div>
</template>
</div>
</template>
</ha-card>
`;
}
moreInfo() { this.fireEvent('hass-more-info'); }
startVaccum() { this.callService(this._config.service.start); }
pauseVacuum() { this.callService(this._config.service.pause); }
stopVacuum() { this.callService(this._config.service.stop); }
locateVacuum() { this.callService(this._config.service.locate); }
returnVacuum() { this.callService(this._config.service.return); }
cleanSpot() { this.callService(this._config.service.spot); }
callService(service) {
this._hass.callService('vacuum', service, {entity_id: this._config.entity});
}
fireEvent(type, options = {}) {
const event = new Event(type, {
bubbles: options.bubbles || true,
cancelable: options.cancelable || true,
composed: options.composed || true,
});
event.detail = {entityId: this._config.entity};
this.shadowRoot.dispatchEvent(event);
return event;
}
getCardSize() {
if (this.name && this.showButtons) return 5;
if (this.name || this.showButtons) return 4;
return 3;
}
setConfig(config) {
const labels = {
status: 'Status',
battery: 'Battery',
mode: 'Mode',
main_brush: 'Main Brush',
side_brush: 'Side Brush',
filter: 'Filter',
sensor: 'Sensor',
hours: 'h',
};
const services = {
start: 'start',
pause: 'pause',
stop: 'stop',
locate: 'locate',
return: 'return_to_base',
spot: 'clean_spot',
};
const buttons = {
start: true,
pause: true,
stop: true,
spot: false,
locate: true,
return: true,
};
const attributes = {
status: 'status',
battery: 'battery_level',
mode: 'fan_speed',
main_brush: 'main_brush_left',
side_brush: 'side_brush_left',
filter: 'filter_left',
sensor: 'sensor_dirty_left',
};
const vendors = {
xiaomi: {
image: '/local/img/vacuum.png',
details: true,
},
valetudo: {
image: '/local/img/vacuum.png',
details: true,
attributes: {
status: 'state',
main_brush: 'mainBrush',
side_brush: 'sideBrush',
filter: 'filter',
sensor: 'sensor',
},
},
ecovacs: {
image: '/local/img/vacuum_ecovacs.png',
details: false,
buttons: {
stop: false,
spot: true,
},
service: {
start: 'turn_on',
pause: 'stop',
stop: 'turn_off',
},
},
deebot: {
image: '/local/img/vacuum_ecovacs.png',
details: true,
service: {
start: 'turn_on',
pause: 'stop',
stop: 'turn_off',
},
attributes: {
main_brush: 'component_main_brush',
side_brush: 'component_side_brush',
filter: 'component_filter',
},
computeValue: v => Math.round(Number(v) / 100),
}
};
if (!config.entity) throw new Error('Please define an entity.');
if (config.entity.split('.')[0] !== 'vacuum') throw new Error('Please define a vacuum entity.');
if (config.vendor && !config.vendor in vendors) throw new Error('Please define a valid vendor.');
const vendor = vendors[config.vendor] || vendors.xiaomi;
this.showDetails = vendor.details;
this.showButtons = config.buttons !== false;
config.service = Object.assign({}, services, vendor.service);
config.buttons = Object.assign({}, buttons, vendor.buttons, config.buttons);
config.attributes = Object.assign({}, attributes, vendor.attributes, config.attributes);
config.labels = Object.assign({}, labels, config.labels);
this.getValue = (field, unit = '') => {
const value = (this.stateObj && config.attributes[field] in this.stateObj.attributes)
? this.stateObj.attributes[config.attributes[field]] + unit
: (this._hass ? this._hass.localize('state.default.unavailable') : 'Unavailable');
return `${config.labels[field]}: ${value}`;
};
this.computeValue = field => {
if (this.stateObj && config.attributes[field] in this.stateObj.attributes) {
const value = this.stateObj.attributes[config.attributes[field]];
return `${config.labels[field]}: ${vendor.computeValue ? vendor.computeValue(value) : value} ${config.labels.hours}`;
} else {
return `${config.labels[field]}: - `;
}
};
this.contentText = `color: ${config.image !== false ? 'white; text-shadow: 0 0 10px black;' : 'var(--primary-text-color)'}`;
this.contentStyle = `padding: ${this.showButtons ? '16px 16px 4px' : '16px'}; ${this.contentText}`;
this.backgroundImage = config.image !== false ? `background-image: url('${config.image || vendor.image}')` : '';
this._config = config;
}
set hass(hass) {
this._hass = hass;
if (hass && this._config) {
this.stateObj = this._config.entity in hass.states ? hass.states[this._config.entity] : null;
if (this.stateObj) {
this.name = this._config.name !== false && (this._config.name || this.stateObj.attributes.friendly_name);
}
}
}
}
customElements.define('xiaomi-vacuum-card', XiaomiVacuumCard);