Skip to content

Commit

Permalink
Merge pull request #5 from custom-cards/cors
Browse files Browse the repository at this point in the history
📺 use new roku remote platform
  • Loading branch information
iantrich authored Jan 25, 2019
2 parents 3c0fb1f + d02f7a0 commit 5be7fa7
Show file tree
Hide file tree
Showing 4 changed files with 157 additions and 123 deletions.
11 changes: 5 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 `<config directory>/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 `<config directory>/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
Expand All @@ -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)
Binary file modified example.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
104 changes: 104 additions & 0 deletions roku-card-editor.js
Original file line number Diff line number Diff line change
@@ -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`
<div class="card-config">
<div class="side-by-side">
<paper-input
label="Name"
.value="${this._name}"
.configValue="${"name"}"
@value-changed="${this._valueChanged}"
></paper-input>
${
customElements.get("ha-entity-picker")
? html`
<ha-entity-picker
.hass="${this.hass}"
.value="${this._entity}"
.configValue=${"entity"}
domain-filter="media_player"
@change="${this._valueChanged}"
allow-custom-entity
></ha-entity-picker>
`
: html`
<paper-input
label="Entity"
.value="${this._entity}"
.configValue="${"entity"}"
@value-changed="${this._valueChanged}"
></paper-input>
`
}
</div>
</div>
`;
}

_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);
165 changes: 48 additions & 117 deletions roku-card.js
Original file line number Diff line number Diff line change
@@ -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() {
Expand All @@ -12,75 +12,61 @@ 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() {
return 1;
}

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()}
<ha-card .header="${this._config.title}">
<ha-card .header="${this._config.name}">
<div class="remote">
${
stateObj.state === "playing"
? html`
<div class="row playing">${stateObj.attributes.source}</div>
`
: ""
}
<div class="row">
<paper-dropdown-menu
label="Input Source"
@value-changed="${this.launchApp}"
>
<paper-listbox
slot="dropdown-content"
.selected="${
stateObj.attributes.source_list.indexOf(
stateObj.attributes.source
)
}"
>
${
stateObj.attributes.source_list.map(source => {
return html`
<paper-item>${source}</paper-item>
`;
})
}
</paper-listbox>
</paper-dropdown-menu>
</div>
<div class="row">
<paper-icon-button
.action="${"back"}"
Expand All @@ -103,34 +89,12 @@ class RokuCard extends LitElement {
</div>
<div class="row">
${
this._apps && this._apps.length > 0
? html`
<img
src="${this._apps[0].icon}"
.app="${this._apps[0].id}"
@click="${this.launchApp}"
/>
`
: ""
}
<paper-icon-button
.action="${"up"}"
@click="${this.handleActionClick}"
icon="mdi:chevron-up"
title="Up"
></paper-icon-button>
${
this._apps && this._apps.length > 1
? html`
<img
src="${this._apps[1].icon}"
.app="${this._apps[1].id}"
@click="${this.launchApp}"
/>
`
: ""
}
</div>
<div class="row">
Expand All @@ -155,39 +119,17 @@ class RokuCard extends LitElement {
</div>
<div class="row">
${
this._apps && this._apps.length > 2
? html`
<img
src="${this._apps[2].icon}"
.app="${this._apps[2].id}"
@click="${this.launchApp}"
/>
`
: ""
}
<paper-icon-button
.action="${"down"}"
@click="${this.handleActionClick}"
icon="mdi:chevron-down"
title="Down"
></paper-icon-button>
${
this._apps && this._apps.length > 3
? html`
<img
src="${this._apps[3].icon}"
.app="${this._apps[3].id}"
@click="${this.launchApp}"
/>
`
: ""
}
</div>
<div class="row">
<paper-icon-button
.action="${"rev"}"
.action="${"reverse"}"
@click="${this.handleActionClick}"
icon="mdi:rewind"
title="Rewind"
Expand All @@ -199,7 +141,7 @@ class RokuCard extends LitElement {
title="Play/Pause"
></paper-icon-button>
<paper-icon-button
.action="${"fwd"}"
.action="${"forward"}"
@click="${this.handleActionClick}"
icon="mdi:fast-forward"
title="Fast-Forward"
Expand All @@ -224,39 +166,28 @@ class RokuCard extends LitElement {
cursor: pointer;
}
img {
border-radius: 25px;
}
.row {
display: flex;
padding: 8px 36px 8px 36px;
justify-content: space-evenly;
}
.row.playing {
padding-bottom: 36px;
font-size: 2.5em;
font-weight: bold;
font-style: italic;
}
</style>
`;
}

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

Expand Down

0 comments on commit 5be7fa7

Please sign in to comment.