/*jshint esversion: 6 */ ((LitElement) => { console.info( '%c BOSCH-INDEGO-CARD %c 1.0.3 ', 'color: lawngreen; background: black; font-weight: bold;', 'color: lawngreen; background: white; font-weight: bold;' ); var INDEGO_SENSOR_START = ""; var INDEGO_SERIAL = ""; var INDEGO_IDENT = ""; const UNKNOWN = "unknown"; const INDEGO_SERVICE = "indego.command"; const state = { status: { entity_suffix: "mower_state_detail", entity: '', key: 'status', icon: 'mdi:robot-mower-outline', }, battery: { entity_suffix: "battery_percentage", entity: '', key: 'battery', unit: '%', icon: 'mdi:battery-charging-80', }, lawn_mowed: { entity_suffix: "lawn_mowed", entity: '', key: 'lawn_mowed', unit: '%', icon: 'mdi:grass', }, }; const attributes = { mowing_mode: { entity_suffix: "mowing_mode", entity: '', key: 'mow_mode', label: 'Mow Mode: ', unit: '', }, next_mow: { entity_suffix: "next_mow", entity: '', key: 'next_mow', label: 'Next Mow: ', unit: ' ', isDate: true }, last_completed: { entity_suffix: "last_completed", entity: '', key: 'last_completed', label: 'Last mow completed: ', unit: ' ago', isDate: true }, mowtime_total: { entity_suffix: "runtime_total", entity: '', key: 'mowtime_total', label: 'MowTime total: ', unit: ' h', }, }; const buttons = { start: { label: 'Start', icon: 'mdi:play', service: INDEGO_SERVICE, service_data: { command: 'mow' }, }, pause: { label: 'Pause', icon: 'mdi:pause', service: INDEGO_SERVICE, service_data: { command: 'pause' }, }, return: { label: 'Return to dock', icon: 'mdi:home-map-marker', service: INDEGO_SERVICE, service_data: { command: 'returnToDock' }, } }; // const compute = { // trueFalse: v => (v === true ? 'Yes' : (v === false ? 'No' : '-')) // }; const html = LitElement.prototype.html; const css = LitElement.prototype.css; class BoschIndegoCard extends LitElement { static get properties() { return { _hass: {}, config: {}, stateObj: {}, }; } 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` ${this.config.show.name ? html`
${this.config.name || this.stateObj.attributes.friendly_name}
` : null} ${(this.config.show.state || this.config.show.attributes) ? html`
${this.config.show.state ? html`
${Object.values(this.config.state).filter(v => v).map(this.renderAttribute.bind(this))}
` : null} ${this.config.show.attributes ? html`
${Object.values(this.config.attributes).filter(v => v).map(this.renderAttribute.bind(this))}
` : null}
` : null} ${this.config.show.buttons ? html`
${Object.values(this.config.buttons).filter(v => v).map(this.renderButton.bind(this))}
` : null}
` : html`Entity '${this.config.entity}' not available...`; } renderAttribute(data) { // const computeFunc = data.compute || (v => v); // const isValidAttribute = data && data.key in this.stateObj.attributes; // const isValidEntityData = data && data.key in this.stateObj; const isValidEntity = data && data.entity in this._hass.states; // const value = isValidAttribute // ? computeFunc(this.stateObj.attributes[data.key]) + (data.unit || '') // : isValidEntityData // ? computeFunc(this.stateObj[data.key]) + (data.unit || '') // : this._hass.localize('state.default.unavailable'); // date = new Date(stateObj.attributes.year, stateObj.attributes.month - 1, stateObj.attributes.day); // formatDate(date, this._hass.language); const value = isValidEntity ? data.hasOwnProperty('isDate') && data.isDate && this._hass.states[data.entity].state != UNKNOWN ? new Date(this._hass.states[data.entity].state).toLocaleDateString(this._hass.language) : this._hass.states[data.entity].state + (data.unit || '') : this._hass.localize('state.default.unavailable'); if(value.trim() != UNKNOWN) { return html`
${data.icon && this.renderIcon(data)}${(data.label || '') + value}
`; } } renderIcon(data) { // const icon = (data.key === 'battery_level' && 'battery_icon' in this.stateObj.attributes) // ? this.stateObj.attributes.battery_icon // : data.icon; const icon = data.icon; return html``; } renderButton(data) { return data && data.show !== false ? html`` : null; } 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'); } find_entities(entityName) { const indegoSensorBegin = /^sensor.\D{2,}_\d{9}_/; const serialNumRegex = /_\d{9}_/; const indegoEntityIdentRegex = /^sensor.\D{2,}_/; INDEGO_SENSOR_START = entityName.match(indegoSensorBegin); if (INDEGO_SENSOR_START != null) { INDEGO_SENSOR_START = INDEGO_SENSOR_START[0]; } else { throw new Error("INDEGO-CARD : Can't find the sensor name in entity."); } INDEGO_SERIAL = entityName.match(serialNumRegex); if (INDEGO_SERIAL != null) { INDEGO_SERIAL = INDEGO_SERIAL[0].substring(1 , 10); } else { throw new Error("INDEGO-CARD : Can't find a valid serial number in entity."); } INDEGO_IDENT = entityName.match(indegoEntityIdentRegex); if (INDEGO_IDENT != null) { INDEGO_IDENT = INDEGO_IDENT[0].substring(7 , INDEGO_IDENT[0].length-1); } else { throw new Error("INDEGO-CARD : Can't find a valid indego identifier in entity."); } // States loop Object.keys(this.config.state).forEach(key => { if (this.config.state[key]) { this.config.state[key].entity = INDEGO_SENSOR_START + this.config.state[key].entity_suffix; } // console.log(key, this.config.state[key]); }); // Attributes loop Object.keys(this.config.attributes).forEach(key => { if(this.config.attributes[key]) { this.config.attributes[key].entity = INDEGO_SENSOR_START + this.config.attributes[key].entity_suffix; } // this.config.attributes[key].entity="sensor.indego_"+INDEGO_SERIAL+this.config.attributes[key].entity_suffix+"_2"; // console.log(key, this.config.attributes[key]); }); //DEBUG // console.log(this.config.state); // console.log(this.config.attributes); } setConfig(config) { if (!config.entity) throw new Error('Please define an entity.'); 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, config.buttons), state: this.deepMerge(state, config.state), attributes: this.deepMerge(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)'};`, button: `--mdc-icon-size: 40px`, content: `padding: ${config.name !== false ? '8px' : '16px'} 16px ${config.buttons !== false ? '8px' : '16px'};`, }, }; this.find_entities(config.entity); } 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, key) { // const mode = e.target.getAttribute('value'); // this.callService(`vacuum.set_${key}`, {entity_id: this.stateObj.entity_id, [key]: 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('bosch-indego-card', BoschIndegoCard); })(window.LitElement || Object.getPrototypeOf(customElements.get("hui-masonry-view") || customElements.get("hui-view"))); // Puts card into the UI card picker dialog (window).customCards = (window).customCards || []; (window).customCards.push({ type: 'bosch-indego-card', name: 'Bosch Indego Card', preview: true, description: 'This Lovelace custom card displays your indego mower information provided by the Indego Integration.', });