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);
 |