Control android tv from the smartphone is realy convenient, but all solutions I found to do this involved having to tap a button on the screen. It works, but having to constantly look at the smartphone to not miss the button is a bit unconfortable.
I came up with this solution (it’s definetly not perfect, but it works): I created a simple trackpad that using javascript detects swipe gestures and call a nodered endpoint that based on the selected source send the right command (this way I can control android tv, satelite receiver and blue ray player).
If someone is interested here is the html code
<html>
<header>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="https://fonts.googleapis.com/icon?family=Material+Icons">
</header>
<style>
body{
margin: 0;
padding: 0;
}
#touchsurface{
width: 100vw;
height: 100vh;
padding: 0;
margin: 0;
background-color: #000;
}
#buttons{
display: none;
width: 100vw;
height: 100vh;
padding: 0;
margin: 0;
}
.toggle{
position: fixed;
right: 2%;
top: 2%;
cursor: pointer;
color: white;
}
#touchIcon{
display: none;
}
.grid-container{
height: 100vh;
display: grid;
grid-template-areas:
'. up .'
'left enter right'
'. down .';
background-color: #000;
}
.grid-container > i {
background-color: rgba(255, 2550, 255, 0.1);
text-align: center;
color: white;
border-radius: 20px;
margin: auto;
font-size: 25vmin;
}
.item1 { grid-area: up; cursor: pointer;}
.item2 { grid-area: left; cursor: pointer;}
.item3 { grid-area: enter; cursor: pointer;}
.item4 { grid-area: right; cursor: pointer;}
.item5 { grid-area: down; cursor: pointer;}
</style>
<span id="buttonsIcon" class="material-icons toggle" onclick="buttons()"> gamepad </span>
<span id="touchIcon" class="material-icons toggle" onclick="touch()"> settings_overscan </span>
<div id="touchsurface"></div>
<div id="buttons">
<div class="grid-container">
<i onclick="up()" class="item1 material-icons"></i>
<i onclick="left()" class="item2 material-icons"></i>
<i onclick="enter()" class="item3 material-icons"></i>
<i onclick="right()" class="item4 material-icons"></i>
<i onclick="down()" class="item5 material-icons"></i>
</div>
</div>
<!-- trackpad / buttons -->
<script>
function buttons() {
var touch = document.getElementById("touchsurface");
var touchIcon = document.getElementById("touchIcon");
var buttons = document.getElementById("buttons");
var buttonsIcon = document.getElementById("buttonsIcon");
touch.style.display = "none";
buttonsIcon.style.display = "none";
buttons.style.display = "block";
touchIcon.style.display = "block";
}
function touch() {
var touch = document.getElementById("touchsurface");
var touchIcon = document.getElementById("touchIcon");
var buttons = document.getElementById("buttons");
var buttonsIcon = document.getElementById("buttonsIcon");
touch.style.display = "block";
buttonsIcon.style.display = "block";
buttons.style.display = "none";
touchIcon.style.display = "none";
}
</script>
<!-- Button get -->
<script>
function up() {
var xmlHttp = new XMLHttpRequest();
xmlHttp.open( "GET", "https://nodered_url_here/endpoint/cinema_up", false );
xmlHttp.send( null );
return xmlHttp.responseText;
}
function down() {
var xmlHttp = new XMLHttpRequest();
xmlHttp.open( "GET", "https://nodered_url_here/endpoint/cinema_down", false );
xmlHttp.send( null );
return xmlHttp.responseText;
}
function left() {
var xmlHttp = new XMLHttpRequest();
xmlHttp.open( "GET", "https://nodered_url_here/endpoint/cinema_left", false );
xmlHttp.send( null );
return xmlHttp.responseText;
}
function right() {
var xmlHttp = new XMLHttpRequest();
xmlHttp.open( "GET", "https://nodered_url_here/endpoint/cinema_right", false );
xmlHttp.send( null );
return xmlHttp.responseText;
}
function enter() {
var xmlHttp = new XMLHttpRequest();
xmlHttp.open( "GET", "https://nodered_url_here/endpoint/cinema_enter", false );
xmlHttp.send( null );
return xmlHttp.responseText;
}
</script>
<!--Swipe detect-->
<script>
function swipedetect(el, callback){
var touchsurface = el,
swipedir,
startX,
startY,
distX,
distY,
threshold = 15, //required min distance traveled to be considered swipe
restraint = 100, // maximum distance allowed at the same time in perpendicular direction
allowedTime = 1000, // maximum time allowed to travel that distance
elapsedTime,
startTime,
handleswipe = callback || function(swipedir){}
touchsurface.addEventListener('touchstart', function(e){
var touchobj = e.changedTouches[0]
swipedir = 'none'
dist = 0
startX = touchobj.pageX
startY = touchobj.pageY
startTime = new Date().getTime() // record time when finger first makes contact with surface
e.preventDefault()
}, false)
touchsurface.addEventListener('touchmove', function(e){
e.preventDefault() // prevent scrolling when inside DIV
}, false)
touchsurface.addEventListener('touchend', function(e){
var touchobj = e.changedTouches[0]
distX = touchobj.pageX - startX // get horizontal dist traveled by finger while in contact with surface
distY = touchobj.pageY - startY // get vertical dist traveled by finger while in contact with surface
elapsedTime = new Date().getTime() - startTime // get time elapsed
if (elapsedTime <= allowedTime){ // first condition for awipe met
if (Math.abs(distX) >= threshold && Math.abs(distY) <= restraint){ // 2nd condition for horizontal swipe met
swipedir = (distX < 0)? 'left' : 'right' // if dist traveled is negative, it indicates left swipe
}
else if (Math.abs(distY) >= threshold && Math.abs(distX) <= restraint){ // 2nd condition for vertical swipe met
swipedir = (distY < 0)? 'up' : 'down' // if dist traveled is negative, it indicates up swipe
}
}
handleswipe(swipedir)
e.preventDefault()
}, false)
}
//USAGE:
/*
var el = document.getElementById('someel')
swipedetect(el, function(swipedir){
swipedir contains either "none", "left", "right", "top", or "down"
if (swipedir =='left')
alert('You just swiped left!')
})
*/
</script>
<!--Event listener-->
<script>
window.addEventListener('load', function(){
var el = document.getElementById('touchsurface')
swipedetect(el, function(swipedir){
// touchsurface.innerHTML = swipedir
if(swipedir == 'up'){
var xmlHttp = new XMLHttpRequest();
xmlHttp.open( "GET", "https://nodered_url_here/endpoint/cinema_up", false ); // false for synchronous request
xmlHttp.send( null );
return xmlHttp.responseText;
}
if(swipedir == 'down'){
var xmlHttp = new XMLHttpRequest();
xmlHttp.open( "GET", "https://nodered_url_here/endpoint/cinema_down", false ); // false for synchronous request
xmlHttp.send( null );
return xmlHttp.responseText;
}
if(swipedir == 'left'){
var xmlHttp = new XMLHttpRequest();
xmlHttp.open( "GET", "https://nodered_url_here/endpoint/cinema_left", false ); // false for synchronous request
xmlHttp.send( null );
return xmlHttp.responseText;
}
if(swipedir == 'right'){
var xmlHttp = new XMLHttpRequest();
xmlHttp.open( "GET", "https://nodered_url_here/endpoint/cinema_right", false ); // false for synchronous request
xmlHttp.send( null );
return xmlHttp.responseText;
}
if(swipedir == 'none'){
var xmlHttp = new XMLHttpRequest();
xmlHttp.open( "GET", "https://nodered_url_here/endpoint/cinema_enter", false ); // false for synchronous request
xmlHttp.send( null );
return xmlHttp.responseText;
}
})
}, false)
</script>
</html>
As said it is not a perfect solution, I think that should be possible to make a custom card to do this, unfortunately I’m not a developer and making a custom card is a bit beyond my capabilities.