275 lines
		
	
	
		
			9.9 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			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);
 | 
