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"))); | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 dfcarvajal
					dfcarvajal