Skip to content

Instantly share code, notes, and snippets.

@soberstadt
Last active February 1, 2021 01:17
Show Gist options
  • Save soberstadt/6e53d693cdc777575b0a4185100b08bd to your computer and use it in GitHub Desktop.
Save soberstadt/6e53d693cdc777575b0a4185100b08bd to your computer and use it in GitHub Desktop.
Home Assistant Roku Remote (requires HA 0.62)
homeassistant:
customize:
script.roku_button:
# using https://github.com/c727/home-assistant-tiles
custom_ui_state_card: state-card-tiles
config:
columns: 3
column_width: 75px
row_height: 75px
entities:
- entity: script.roku_button
icon: mdi:power
column_span: 3
data: { button: power }
- entity: script.roku_button
icon: mdi:backburger
data: { button: back }
- entity: script.roku_button
icon: mdi:home-outline
column: 3
data: { button: home }
- entity: script.roku_button
icon: mdi:arrow-up-thick
column: 2
data: { button: up }
- entity: script.roku_button
icon: mdi:arrow-left-thick
column: 1
data: { button: left}
- entity: script.roku_button
label: "OK"
column: 2
data: { button: select }
- entity: script.roku_button
icon: mdi:arrow-right-thick
column: 3
data: { button: right }
- entity: script.roku_button
icon: mdi:arrow-down-thick
column: 2
data: { button: down }
- entity: script.roku_button
icon: mdi:replay
column: 1
data: { button: replay }
- entity: script.roku_button
icon: mdi:information-outline
column: 3
data: { button: info }
- entity: script.roku_button
icon: mdi:rewind
column: 1
data: { button: reverse }
- entity: script.roku_button
icon: mdi:play-pause
column: 2
data: { button: play }
- entity: script.roku_button
icon: mdi:fast-forward
column: 3
data: { button: forward }
- entity: script.roku_button
icon: mdi:volume-plus
column: 4
column_span: 1
row: 1
row_span: 2
data: { button: volume_up }
- entity: script.roku_button
icon: mdi:volume-minus
column: 4
column_span: 1
row: 3
row_span: 2
data: { button: volume_down }
- entity: script.roku_button
icon: mdi:volume-mute
column: 4
column_span: 1
row: 5
data: { button: volume_mute }
frontend:
extra_html_url:
- /local/custom_ui/state-card-tiles.html
extra_html_url_es5:
- /local/custom_ui/state-card-tiles.html
group:
Living Room TV:
entities:
- script.roku_button
script:
roku_button:
sequence:
service: roku_remote.press_button
data_template:
button: "{{ button }}"
python_script:
input_text:
roku_remote_ip:
name: Roku Remote IP
roku_remote:
# This file must be placed in
# {config-dir}/custom_components
# requires python-roku v3.1.5 which will be released with HA 0.62
DOMAIN = 'roku_remote'
LAST_BUTTON_TOPIC = 'last_button'
from roku import Roku
import logging
def setup(hass, config):
roku = None
last_ip_state_name = 'input_text.roku_remote_ip'
def find_roku():
logging.info('[roku_remote] looking for devices')
devices = Roku.discover(timeout=5)
logging.info('[roku_remote] found: ')
logging.info(devices)
found = None;
for d in devices:
if d.port == 8060: found = d
if found != None: break
if found != None:
roku = found
hass.states.set(last_ip_state_name, roku.host)
logging.info('[roku_remote] selected:')
logging.info(roku)
else:
logging.error('[roku_remote] no roku found')
return found
find_roku()
if roku == None:
roku = Roku(hass.states.get(last_ip_state_name).state)
def button_press(call):
button_name = call.data.get('button', '')
hass.states.set("{0}.{1}".format(DOMAIN, LAST_BUTTON_TOPIC), button_name)
try:
getattr(roku, button_name)()
except Exception as e:
if find_roku() != None: getattr(roku, button_name)()
hass.services.async_register(DOMAIN, 'press_button', button_press)
return True
# This file must be placed in
# {config-dir}/python_scripts
#
# Some extra fun, since there is no api for setting tv volume, instead we can turn it down a whole bunch
# and then go up for the number of times specificed in service call.
for x in range(0, 50):
hass.services.call('roku_remote', 'press_button', service_data={ 'button': "volume_down" }, blocking=True)
level = data.get('level', 15)
for x in range(0, level):
hass.services.call('roku_remote', 'press_button', service_data={ 'button': "volume_up" }, blocking=True)
<!--
This file must be placed in
{config-dir}/www/custom_ui/
Created by @c727
https://github.com/c727/home-assistant-tiles
-->
<dom-module id="state-card-tiles">
<template>
<style>
.grid {
display: grid;
grid-template-columns: repeat(var(--tiles-columns), var(--tiles-column-width));
grid-auto-rows: var(--tiles-row-height);
grid-gap: var(--tiles-gap);
width: 100%;
}
paper-button {
box-shadow: none !important;
margin: 0 !important;
background-color: var(--tiles-color);
color: #fff;
}
paper-button.on {
background-color: var(--tiles-color-on);
}
paper-button.off {
background-color: var(--tiles-color-off);
}
</style>
<paper-button-group class="grid" on-tap="stopPropagation">
<template is="dom-repeat" items="[[config.entities]]" as="entity">
<paper-button raised class$="[[computeTileClass(hass, entity)]]" style$="[[computeTileStyle(entity)]]"
on-tap="callService" >
<template is="dom-if" if="[[entity.icon]]">
<iron-icon icon="[[entity.icon]]"></iron-icon>
</template>
[[entity.label]]
</paper-button>
</template>
</paper-button-group>
</template>
</dom-module>
<script>
const VERSION = '2017113';
var timeout = 0;
Polymer({
is: 'state-card-tiles',
properties: {
hass: {
type: Object,
},
stateObj: {
type: Object,
},
config: {
type: Object,
computed: 'computeConfig(stateObj)',
},
},
computeConfig: function (stateObj) {
return stateObj.attributes.config;
},
ready: function () {
var config = this.config;
this.updateStyles({
'--tiles-columns': config.columns ? config.columns : '3',
'--tiles-column-width': config.column_width ? config.column_width : '100px',
'--tiles-row-height': config.row_height ? config.row_height : '100px',
'--tiles-gap': config.gap ? config.gap : '4px',
'--tiles-color': config.color ? config.color : 'var(--primary-color)',
'--tiles-color-on': config.color_on ? config.color_on : 'var(--google-green-500)',
'--tiles-color-off': config.color_off ? config.color_off : 'var(--google-red-500)',
});
},
computeTileClass: function (hass, entity) {
var domain = entity.entity.split('.')[0];
if (domain === 'script') return '';
return (hass.states[entity.entity].state === 'on') ? 'on' : 'off';
},
computeTileStyle: function (entity) {
var c = entity.column ? entity.column : 'auto';
var cs = entity.column_span ? entity.column_span : 1;
var r = entity.row ? entity.row : 'auto';
var rs = entity.row_span ? entity.row_span : 1;
var img = entity.image ? ' background-image: url("' + entity.image + '");' : '';
return 'grid-column: ' + c + ' / span ' + cs + '; grid-row: ' + r + ' / span ' + rs + ';' + img;
},
callService: function (e) {
var entity = e.model.entity.entity;
var domain = entity.split('.')[0];
if (domain === 'script') {
var service = entity.split('.')[1];
var data = e.model.entity.data ? (e.model.entity.data) : {};
} else {
var service = 'toggle';
var data = { 'entity_id': entity };
}
this.hass.callService(domain, service, data);
},
stopPropagation: function (e) {
e.stopPropagation();
},
});
</script>
@svkowalski
Copy link

I found this custom Roku Remote to be an excellent way to add a capability to my Home Assistant configuration that I've wanted to do for a long time. I thought I'd point out a couple areas that initially confused me.

I have 3 Roku's, but I'm only trying to control 1 of them with roku_remote. However, I could see that one of my other Rokus was being enabled, probably because discovery found all 3, even though I'm only using the one in HA. I finally realized that if I replaced the string "Roku Remote IP":

input_text:
  roku_remote_ip:
    name: Roku Remote IP

...with my Roku's actual IP address, I could lock in the one I was actually trying to control. So I'm assuming that your configuration.yaml file intended for that string to be replaced, and not entered literally?

The second point of confusion is the reference to:

https://github.com/c727/home-assistant-tiles

I get a 404 error when I try to access that repo. There is another version of your configuration.yaml that has that URL exposed in the python code.

Finally, I really like the nice-looking set of buttons you built using customization, but I can't get them to work. I found a couple variants of the above .yaml code floating around; perhaps this is tied to a more recent or forked version of roku_remote.py?

I'll keep working with this to get it all working for me. I just wanted to say thanks, again for putting this together. Great job!

Steve K.

@jbeck22
Copy link

jbeck22 commented Feb 1, 2019

I have this added to HA and I have verified that the configuration is "good" from within HA, but where do I go inside of HA to add the remote?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment