Upgrades HACS
This commit is contained in:
@@ -1,274 +1,412 @@
|
||||
class XiaomiVacuumCard extends Polymer.Element {
|
||||
((LitElement) => {
|
||||
console.info(
|
||||
'%c XIAOMI-VACUUM-CARD %c 4.1.0 ',
|
||||
'color: cyan; background: black; font-weight: bold;',
|
||||
'color: darkblue; background: white; font-weight: bold;',
|
||||
);
|
||||
|
||||
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>
|
||||
`;
|
||||
const state = {
|
||||
status: {
|
||||
key: 'status',
|
||||
icon: 'mdi:robot-vacuum',
|
||||
},
|
||||
battery: {
|
||||
key: 'battery_level',
|
||||
unit: '%',
|
||||
icon: 'mdi:battery-charging-80',
|
||||
},
|
||||
mode: {
|
||||
key: 'fan_speed',
|
||||
icon: 'mdi:fan',
|
||||
},
|
||||
};
|
||||
|
||||
const attributes = {
|
||||
main_brush: {
|
||||
key: 'main_brush_left',
|
||||
label: 'Main Brush: ',
|
||||
unit: ' h',
|
||||
},
|
||||
side_brush: {
|
||||
key: 'side_brush_left',
|
||||
label: 'Side Brush: ',
|
||||
unit: ' h',
|
||||
},
|
||||
filter: {
|
||||
key: 'filter_left',
|
||||
label: 'Filter: ',
|
||||
unit: ' h',
|
||||
},
|
||||
sensor: {
|
||||
key: 'sensor_dirty_left',
|
||||
label: 'Sensor: ',
|
||||
unit: ' h',
|
||||
},
|
||||
};
|
||||
|
||||
const buttons = {
|
||||
start: {
|
||||
label: 'Start',
|
||||
icon: 'mdi:play',
|
||||
service: 'vacuum.start',
|
||||
},
|
||||
pause: {
|
||||
label: 'Pause',
|
||||
icon: 'mdi:pause',
|
||||
service: 'vacuum.pause',
|
||||
},
|
||||
stop: {
|
||||
label: 'Stop',
|
||||
icon: 'mdi:stop',
|
||||
service: 'vacuum.stop',
|
||||
},
|
||||
spot: {
|
||||
show: false,
|
||||
label: 'Clean Spot',
|
||||
icon: 'mdi:broom',
|
||||
service: 'vacuum.clean_spot',
|
||||
},
|
||||
locate: {
|
||||
label: 'Locate',
|
||||
icon: 'mdi:map-marker',
|
||||
service: 'vacuum.locate',
|
||||
},
|
||||
return: {
|
||||
label: 'Return to Base',
|
||||
icon: 'mdi:home-map-marker',
|
||||
service: 'vacuum.return_to_base',
|
||||
},
|
||||
};
|
||||
|
||||
const compute = {
|
||||
trueFalse: v => (v === true ? 'Yes' : (v === false ? 'No' : '-')),
|
||||
divide100: v => Math.round(Number(v) / 100),
|
||||
}
|
||||
|
||||
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',
|
||||
const vendors = {
|
||||
xiaomi: {},
|
||||
xiaomi_mi: {
|
||||
attributes: {
|
||||
main_brush: {key: 'main_brush_hours'},
|
||||
side_brush: {key: 'side_brush_hours'},
|
||||
filter: {key: 'hypa_hours'},
|
||||
sensor: {
|
||||
key: 'mop_hours',
|
||||
label: 'Mop: ',
|
||||
},
|
||||
},
|
||||
ecovacs: {
|
||||
image: '/local/img/vacuum_ecovacs.png',
|
||||
details: false,
|
||||
buttons: {
|
||||
stop: false,
|
||||
spot: true,
|
||||
},
|
||||
service: {
|
||||
start: 'turn_on',
|
||||
pause: 'stop',
|
||||
stop: 'turn_off',
|
||||
},
|
||||
valetudo: {
|
||||
state: {
|
||||
status: {
|
||||
key: 'state',
|
||||
},
|
||||
},
|
||||
deebot: {
|
||||
image: '/local/img/vacuum_ecovacs.png',
|
||||
details: true,
|
||||
service: {
|
||||
start: 'turn_on',
|
||||
pause: 'stop',
|
||||
stop: 'turn_off',
|
||||
attributes: {
|
||||
main_brush: {key: 'mainBrush'},
|
||||
side_brush: {key: 'sideBrush'},
|
||||
filter: {key: 'filter'},
|
||||
sensor: {key: 'sensor'},
|
||||
},
|
||||
},
|
||||
roomba: {
|
||||
attributes: {
|
||||
main_brush: false,
|
||||
side_brush: false,
|
||||
filter: false,
|
||||
sensor: false,
|
||||
bin_present: {
|
||||
key: 'bin_present',
|
||||
label: 'Bin Present: ',
|
||||
compute: compute.trueFalse,
|
||||
},
|
||||
attributes: {
|
||||
main_brush: 'component_main_brush',
|
||||
side_brush: 'component_side_brush',
|
||||
filter: 'component_filter',
|
||||
bin_full: {
|
||||
key: 'bin_full',
|
||||
label: 'Bin Full: ',
|
||||
compute: compute.trueFalse,
|
||||
},
|
||||
computeValue: v => Math.round(Number(v) / 100),
|
||||
}
|
||||
};
|
||||
},
|
||||
},
|
||||
robovac: {
|
||||
attributes: false,
|
||||
buttons: {
|
||||
stop: {show: false},
|
||||
spot: {show: true},
|
||||
},
|
||||
},
|
||||
ecovacs: {
|
||||
attributes: false,
|
||||
buttons: {
|
||||
start: {service: 'vacuum.turn_on'},
|
||||
pause: {service: 'vacuum.stop'},
|
||||
stop: {service: 'vacuum.turn_off', show: false},
|
||||
spot: {show: true},
|
||||
},
|
||||
},
|
||||
deebot: {
|
||||
buttons: {
|
||||
start: {service: 'vacuum.turn_on'},
|
||||
pause: {service: 'vacuum.stop'},
|
||||
stop: {service: 'vacuum.turn_off'},
|
||||
},
|
||||
attributes: {
|
||||
main_brush: {
|
||||
key: 'component_main_brush',
|
||||
compute: compute.divide100,
|
||||
},
|
||||
side_brush: {
|
||||
key: 'component_side_brush',
|
||||
compute: compute.divide100,
|
||||
},
|
||||
filter: {
|
||||
key: 'component_filter',
|
||||
compute: compute.divide100,
|
||||
},
|
||||
sensor: false,
|
||||
},
|
||||
},
|
||||
deebot_slim: {
|
||||
buttons: {
|
||||
start: {service: 'vacuum.turn_on'},
|
||||
pause: {service: 'vacuum.stop'},
|
||||
stop: {service: 'vacuum.turn_off'},
|
||||
},
|
||||
attributes: {
|
||||
main_brush: false,
|
||||
side_brush: {key: 'component_side_brush'},
|
||||
filter: {key: 'component_filter'},
|
||||
sensor: false,
|
||||
},
|
||||
},
|
||||
neato: {
|
||||
state: {
|
||||
mode: false,
|
||||
},
|
||||
attributes: {
|
||||
main_brush: false,
|
||||
side_brush: false,
|
||||
filter: false,
|
||||
sensor: false,
|
||||
clean_area: {
|
||||
key: 'clean_area',
|
||||
label: 'Cleaned area: ',
|
||||
unit: ' m2',
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
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 html = LitElement.prototype.html;
|
||||
const css = LitElement.prototype.css;
|
||||
|
||||
const vendor = vendors[config.vendor] || vendors.xiaomi;
|
||||
class XiaomiVacuumCard extends LitElement {
|
||||
|
||||
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);
|
||||
static get properties() {
|
||||
return {
|
||||
_hass: {},
|
||||
config: {},
|
||||
stateObj: {},
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define('xiaomi-vacuum-card', XiaomiVacuumCard);
|
||||
static get styles() {
|
||||
return css`
|
||||
.background {
|
||||
background-repeat: no-repeat;
|
||||
background-position: center center;
|
||||
background-size: cover;
|
||||
}
|
||||
.title {
|
||||
font-size: 20px;
|
||||
padding: 12px 16px 8px;
|
||||
text-align: center;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
}
|
||||
.flex {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-evenly;
|
||||
}
|
||||
.grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, auto);
|
||||
cursor: pointer;
|
||||
}
|
||||
.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);
|
||||
}`;
|
||||
}
|
||||
|
||||
render() {
|
||||
return this.stateObj ? html`
|
||||
<ha-card class="background" style="${this.config.styles.background}">
|
||||
${this.config.show.name ?
|
||||
html`<div class="title">${this.config.name || this.stateObj.attributes.friendly_name}</div>`
|
||||
: null}
|
||||
${(this.config.show.state || this.config.show.attributes) ? html`
|
||||
<div class="grid" style="${this.config.styles.content}" @click="${() => this.fireEvent('hass-more-info')}">
|
||||
${this.config.show.state ? html`
|
||||
<div class="grid-content grid-left">
|
||||
${Object.values(this.config.state).filter(v => v).map(this.renderAttribute.bind(this))}
|
||||
</div>` : null}
|
||||
${this.config.show.attributes ? html`
|
||||
<div class="grid-content grid-right">
|
||||
${Object.values(this.config.attributes).filter(v => v).map(this.renderAttribute.bind(this))}
|
||||
</div>` : null}
|
||||
</div>` : null}
|
||||
${this.config.show.buttons ? html`
|
||||
<div class="flex">
|
||||
${Object.values(this.config.buttons).filter(v => v).map(this.renderButton.bind(this))}
|
||||
</div>` : null}
|
||||
</ha-card>` : html`<ha-card style="padding: 8px 16px">Entity '${this.config.entity}' not available...</ha-card>`;
|
||||
}
|
||||
|
||||
renderAttribute(data) {
|
||||
const computeFunc = data.compute || (v => v);
|
||||
const isValid = data && data.key in this.stateObj.attributes;
|
||||
|
||||
const value = isValid
|
||||
? computeFunc(this.stateObj.attributes[data.key]) + (data.unit || '')
|
||||
: this._hass.localize('state.default.unavailable');
|
||||
const attribute = html`<div>${data.icon && this.renderIcon(data)}${(data.label || '') + value}</div>`;
|
||||
|
||||
return (isValid && data.key === 'fan_speed' && 'fan_speed_list' in this.stateObj.attributes)
|
||||
? this.renderMode(attribute) : attribute;
|
||||
|
||||
}
|
||||
|
||||
renderIcon(data) {
|
||||
const icon = (data.key === 'battery_level' && 'battery_icon' in this.stateObj.attributes)
|
||||
? this.stateObj.attributes.battery_icon
|
||||
: data.icon;
|
||||
return html`<ha-icon icon="${icon}" style="margin-right: 10px; ${this.config.styles.icon}"></ha-icon>`;
|
||||
}
|
||||
|
||||
renderButton(data) {
|
||||
return data && data.show !== false
|
||||
? html`<ha-icon-button
|
||||
@click="${() => this.callService(data.service, data.service_data)}"
|
||||
icon="${data.icon}"
|
||||
title="${data.label || ''}"
|
||||
style="${this.config.styles.icon}"></ha-icon-button>`
|
||||
: null;
|
||||
}
|
||||
|
||||
renderMode(attribute) {
|
||||
const selected = this.stateObj.attributes.fan_speed;
|
||||
const list = this.stateObj.attributes.fan_speed_list;
|
||||
|
||||
return html`
|
||||
<paper-menu-button slot="dropdown-trigger" @click="${e => e.stopPropagation()}" style="padding: 0">
|
||||
<paper-button slot="dropdown-trigger">${attribute}</paper-button>
|
||||
<paper-listbox slot="dropdown-content" selected="${list.indexOf(selected)}" @click="${e => this.handleChange(e)}">
|
||||
${list.map(item => html`<paper-item value="${item}" style="text-shadow: none;">${item}</paper-item>`)}
|
||||
</paper-listbox>
|
||||
</paper-menu-button>
|
||||
`;
|
||||
}
|
||||
|
||||
getCardSize() {
|
||||
if (this.config.show.name && this.config.show.buttons) return 4;
|
||||
if (this.config.show.name || this.config.show.buttons) return 3;
|
||||
return 2;
|
||||
}
|
||||
|
||||
shouldUpdate(changedProps) {
|
||||
return changedProps.has('stateObj');
|
||||
}
|
||||
|
||||
setConfig(config) {
|
||||
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.config = {
|
||||
name: config.name,
|
||||
entity: config.entity,
|
||||
show: {
|
||||
name: config.name !== false,
|
||||
state: config.state !== false,
|
||||
attributes: config.attributes !== false,
|
||||
buttons: config.buttons !== false,
|
||||
},
|
||||
buttons: this.deepMerge(buttons, vendor.buttons, config.buttons),
|
||||
state: this.deepMerge(state, vendor.state, config.state),
|
||||
attributes: this.deepMerge(attributes, vendor.attributes, config.attributes),
|
||||
styles: {
|
||||
background: config.image ? `background-image: url('${config.image}'); color: white; text-shadow: 0 0 10px black;` : '',
|
||||
icon: `color: ${config.image ? 'white' : 'var(--paper-item-icon-color)'};`,
|
||||
content: `padding: ${config.name !== false ? '8px' : '16px'} 16px ${config.buttons !== false ? '8px' : '16px'};`,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
set hass(hass) {
|
||||
if (hass && this.config) {
|
||||
this.stateObj = this.config.entity in hass.states ? hass.states[this.config.entity] : null;
|
||||
}
|
||||
this._hass = hass;
|
||||
}
|
||||
|
||||
handleChange(e) {
|
||||
const mode = e.target.getAttribute('value');
|
||||
this.callService('vacuum.set_fan_speed', {entity_id: this.stateObj.entity_id, fan_speed: mode});
|
||||
}
|
||||
|
||||
callService(service, data = {entity_id: this.stateObj.entity_id}) {
|
||||
const [domain, name] = service.split('.');
|
||||
this._hass.callService(domain, name, data);
|
||||
}
|
||||
|
||||
fireEvent(type, options = {}) {
|
||||
const event = new Event(type, {
|
||||
bubbles: options.bubbles || true,
|
||||
cancelable: options.cancelable || true,
|
||||
composed: options.composed || true,
|
||||
});
|
||||
event.detail = {entityId: this.stateObj.entity_id};
|
||||
this.dispatchEvent(event);
|
||||
}
|
||||
|
||||
deepMerge(...sources) {
|
||||
const isObject = (obj) => obj && typeof obj === 'object';
|
||||
const target = {};
|
||||
|
||||
sources.filter(source => isObject(source)).forEach(source => {
|
||||
Object.keys(source).forEach(key => {
|
||||
const targetValue = target[key];
|
||||
const sourceValue = source[key];
|
||||
|
||||
if (Array.isArray(targetValue) && Array.isArray(sourceValue)) {
|
||||
target[key] = targetValue.concat(sourceValue);
|
||||
} else if (isObject(targetValue) && isObject(sourceValue)) {
|
||||
target[key] = this.deepMerge(Object.assign({}, targetValue), sourceValue);
|
||||
} else {
|
||||
target[key] = sourceValue;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
return target;
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define('xiaomi-vacuum-card', XiaomiVacuumCard);
|
||||
})(window.LitElement || Object.getPrototypeOf(customElements.get("hui-masonry-view") || customElements.get("hui-view")));
|
||||
|
||||
Binary file not shown.
Reference in New Issue
Block a user