diff --git a/README.md b/README.md index 225d2b9..7406e5a 100644 --- a/README.md +++ b/README.md @@ -22,20 +22,20 @@ This card is for [Lovelace](https://www.home-assistant.io/lovelace) on [Home Ass | ---- | ---- | ------- | ----------- | type | string | **Required** | `custom:roku-card` | entity | string | **Required** | `media_player` entity of Roku device -| ip_address | string | **Required** | IP address of the Roku device -| title | string | **Optional** | Card title +| name | string | **Optional** | Card name ## Installation ### Step 1 -Save [roku-card](https://github.com/custom-cards/roku-card/raw/master/roku-card.js) to `/www/roku-card.js` on your Home Assistant instanse. +Install `roku-card` by copying `roku-card.js` and `roku-card-editor.js` from this repo to `/www/roku-card.js` on your Home Assistant instance. **Example:** ```bash wget https://raw.githubusercontent.com/custom-cards/roku-card/master/roku-card.js -mv roku-card.js /config/www/ +wget https://raw.githubusercontent.com/custom-cards/roku-card/master/roku-card-editor.js +mv roku-card* /config/www/ ``` ### Step 2 @@ -55,8 +55,7 @@ Add a custom element in your `ui-lovelace.yaml` ```yaml - type: custom:roku-card entity: media_player.bedroom_tv - title: Bedroom TV - ip_address: 'http://192.168.1.4' + name: Bedroom TV ``` [Troubleshooting](https://github.com/thomasloven/hass-config/wiki/Lovelace-Plugins) diff --git a/example.png b/example.png index be4b30d..2502d46 100644 Binary files a/example.png and b/example.png differ diff --git a/roku-card-editor.js b/roku-card-editor.js new file mode 100644 index 0000000..f10fec2 --- /dev/null +++ b/roku-card-editor.js @@ -0,0 +1,104 @@ +if (!customElements.get("paper-input")) { + console.log("imported", "paper-input"); + import("https://unpkg.com/@polymer/paper-input/paper-input.js?module"); +} + +const fireEvent = (node, type, detail, options) => { + options = options || {}; + detail = detail === null || detail === undefined ? {} : detail; + const event = new Event(type, { + bubbles: options.bubbles === undefined ? true : options.bubbles, + cancelable: Boolean(options.cancelable), + composed: options.composed === undefined ? true : options.composed + }); + event.detail = detail; + node.dispatchEvent(event); + return event; +}; + +const LitElement = Object.getPrototypeOf( + customElements.get("ha-panel-lovelace") +); +const html = LitElement.prototype.html; + +export class RokuCardEditor extends LitElement { + setConfig(config) { + this._config = config; + } + + static get properties() { + return { hass: {}, _config: {} }; + } + + get _name() { + return this._config.name || ""; + } + + + get _entity() { + return this._config.entity || ""; + } + + render() { + if (!this.hass) { + return html``; + } + + return html` +
+
+ + ${ + customElements.get("ha-entity-picker") + ? html` + + ` + : html` + + ` + } +
+
+ `; + } + + _valueChanged(ev) { + if (!this._config || !this.hass) { + return; + } + const target = ev.target; + if (this[`_${target.configValue}`] === target.value) { + return; + } + if (target.configValue) { + if (target.value === "") { + delete this._config[target.configValue]; + } else { + this._config = { + ...this._config, + [target.configValue]: target.value + }; + } + } + fireEvent(this, "config-changed", { config: this._config }); + } +} + +customElements.define("roku-card-editor", RokuCardEditor); \ No newline at end of file diff --git a/roku-card.js b/roku-card.js index db714b0..409048f 100644 --- a/roku-card.js +++ b/roku-card.js @@ -1,7 +1,7 @@ -var LitElement = - LitElement || - Object.getPrototypeOf(customElements.get("hui-error-entity-row")); -var html = LitElement.prototype.html; +const LitElement = Object.getPrototypeOf( + customElements.get("ha-panel-lovelace") +); +const html = LitElement.prototype.html; class RokuCard extends LitElement { static get properties() { @@ -12,8 +12,13 @@ class RokuCard extends LitElement { }; } - constructor() { - super(); + static async getConfigElement() { + await import("./roku-card-editor.js"); + return document.createElement("roku-card-editor"); + } + + static getStubConfig() { + return {}; } getCardSize() { @@ -21,66 +26,47 @@ class RokuCard extends LitElement { } setConfig(config) { - if (!config.entity || !config.ip_address) { + if (!config.entity) { console.log("Invalid configuration"); return; } this._config = config; - - const HttpApps = new XMLHttpRequest(); - HttpApps.open("GET", this._config.ip_address + ":8060/query/apps"); - HttpApps.send(); - HttpApps.onreadystatechange = e => { - if (HttpApps.responseXML) { - const appsXml = HttpApps.responseXML; - const tags = appsXml.getElementsByTagName("app"); - let apps = []; - for (let i = 0; i < tags.length && i < 4; i++) { - const HttpIcon = new XMLHttpRequest(); - HttpIcon.open( - "GET", - this._config.ip_address + - ":8060/query/icon/" + - String(tags[i].getAttribute("id")) - ); - HttpIcon.responseType = "arraybuffer"; - HttpIcon.onreadystatechange = e => { - if (HttpIcon.response) { - const data = new Uint8Array(HttpIcon.response); - const raw = String.fromCharCode.apply(null, data); - const base64 = btoa(raw); - apps.push({ - id: tags[i].getAttribute("id"), - name: tags[i].childNodes[0].nodeValue, - icon: "data:image/jpeg;base64," + base64 - }); - } - }; - HttpIcon.send(); - } - this._apps = apps; - } - }; } render() { - if (!this._config || !this._apps || !this.hass) { + if (!this._config || !this.hass) { return html``; } const stateObj = this.hass.states[this._config.entity]; return html` ${this.renderStyle()} - +
- ${ - stateObj.state === "playing" - ? html` -
${stateObj.attributes.source}
- ` - : "" - } +
+ + + ${ + stateObj.attributes.source_list.map(source => { + return html` + ${source} + `; + }) + } + + +
- ${ - this._apps && this._apps.length > 0 - ? html` - - ` - : "" - } - ${ - this._apps && this._apps.length > 1 - ? html` - - ` - : "" - }
@@ -155,39 +119,17 @@ class RokuCard extends LitElement {
- ${ - this._apps && this._apps.length > 2 - ? html` - - ` - : "" - } - ${ - this._apps && this._apps.length > 3 - ? html` - - ` - : "" - }
`; } launchApp(e) { - const Http = new XMLHttpRequest(); - const url = this._config.ip_address + ":8060/launch/" + e.currentTarget.app; - Http.open("POST", url); - Http.send(); + this.hass.callService("media_player", "select_source", { + entity_id: this._config.entity, + source: e.currentTarget.value + }); } handleActionClick(e) { - const Http = new XMLHttpRequest(); - const url = - this._config.ip_address + ":8060/keypress/" + e.currentTarget.action; - Http.open("POST", url); - Http.send(); + let remote = this._config.entity.split(".")[1]; + this.hass.callService("remote", "send_command", { + entity_id: "remote." + remote, + command: e.currentTarget.action + }); } }