Added Samsung smart TV web plugin

This commit is contained in:
Fabio Manganiello 2020-05-03 17:21:16 +02:00
parent 2f57ccf412
commit d0ccd65214
6 changed files with 427 additions and 55 deletions

View file

@ -0,0 +1,73 @@
@import 'common/vars';
.tv-samsung-ws-container {
height: 100%;
display: flex;
justify-content: center;
.controls {
max-width: 500px;
height: calc(100% - .15em);
display: flex;
align-items: center;
justify-content: center;
flex-direction: column;
padding: 0 1em;
border: $default-border-2;
border-radius: 2em;
box-shadow: 0 2px 9px 2px #bbb;
.power { color: red; }
button { margin: .5em 0; }
}
.rows {
flex-direction: column;
* {
button {
width: 100%;
}
}
}
.section {
width: 100%;
display: flex;
margin: 0 0 1.5em 0;
padding: 0 0 1.5em 0;
border-bottom: $default-border-2;
}
.directions {
flex-direction: column;
* {
button {
width: 5em;
height: 4em;
}
}
}
.volume, .channel {
text-align: center;
}
.colors {
text-align: center;
.color {
width: 3em;
height: 3em;
padding: 0;
border-radius: 2em;
&.red { background: red; }
&.green { background: green; }
&.yellow { background: yellow; }
&.blue { background: blue; }
}
}
}

View file

@ -0,0 +1,115 @@
Vue.component('tv-samsung-ws', {
template: '#tmpl-tv-samsung-ws',
data: function() {
return {};
},
methods: {
up: async function() {
await request('tv.samsung.ws.up');
},
down: async function() {
await request('tv.samsung.ws.down');
},
left: async function() {
await request('tv.samsung.ws.left');
},
right: async function() {
await request('tv.samsung.ws.right');
},
enter: async function() {
await request('tv.samsung.ws.enter');
},
power: async function() {
await request('tv.samsung.ws.power');
},
red: async function() {
await request('tv.samsung.ws.red');
},
yellow: async function() {
await request('tv.samsung.ws.yellow');
},
green: async function() {
await request('tv.samsung.ws.green');
},
blue: async function() {
await request('tv.samsung.ws.blue');
},
volumeUp: async function() {
await request('tv.samsung.ws.volume_up');
},
volumeDown: async function() {
await request('tv.samsung.ws.volume_down');
},
channelUp: async function() {
await request('tv.samsung.ws.channel_up');
},
channelDown: async function() {
await request('tv.samsung.ws.channel_down');
},
mute: async function() {
await request('tv.samsung.ws.mute');
},
home: async function() {
await request('tv.samsung.ws.home');
},
back: async function() {
await request('tv.samsung.ws.back');
},
menu: async function() {
await request('tv.samsung.ws.menu');
},
info: async function() {
await request('tv.samsung.ws.info');
},
source: async function() {
await request('tv.samsung.ws.source');
},
tools: async function() {
await request('tv.samsung.ws.tools');
},
browser: async function() {
const url = prompt('URL to open').trim();
if (!url.length) {
return;
}
await request('tv.samsung.ws.open_browser', {'url': url});
},
channel: async function() {
const ch = prompt('Channel number').trim();
if (!ch.length) {
return;
}
await request('tv.samsung.ws.channel', {channel: parseInt(ch)});
},
color: async function(event) {
await request('tv.samsung.ws.' + event.target.value);
}
},
});

View file

@ -19,6 +19,7 @@
'switches': 'fa fa-toggle-on',
'tts': 'fa fa-comment',
'tts.google': 'fa fa-comment',
'tv.samsung.ws': 'fas fa-tv',
'zigbee.mqtt': 'fa fa-zigbee',
'zwave': 'fa fa-zwave',
}

View file

@ -2,7 +2,7 @@
<div class="gpio-container">
<div class="refresh pull-right">
<button class="btn btn-default btn-refresh" title="Refresh">
<i class="fa fa-retweet">
<i class="fa fa-retweet"></i>
</button>
</div>

View file

@ -0,0 +1,158 @@
<script type="text/x-template" id="tmpl-tv-samsung-ws">
<div class="tv-samsung-ws-container">
<div class="controls" tabindex="1">
<div class="row section">
<div class="col-4">
<button @click="home" value="home">
<i class="fas fa-home"></i>
</button>
</div>
<div class="col-4">&nbsp;</div>
<div class="col-4">
<button @click="power" class="power" value="power">
<i class="fas fa-power-off"></i>
</button>
</div>
</div>
<div class="row rows section">
<div class="row">
<div class="col-4">
<button @click="back" value="back">
<i class="fas fa-undo"></i>
</button>
</div>
<div class="col-4">
<button @click="info" value="info">
<i class="fas fa-info"></i>
</button>
</div>
<div class="col-4">
<button @click="source" value="source">
<i class="fas fa-plug"></i>
</button>
</div>
</div>
<div class="row">
<div class="col-4">
<button @click="menu" value="menu">
<i class="fas fa-bars"></i>
</button>
</div>
<div class="col-4">
<button @click="tools" value="tools">
<i class="fas fa-tools"></i>
</button>
</div>
<div class="col-4">
<button @click="browser" value="browser">
<i class="fas fa-globe"></i>
</button>
</div>
</div>
</div>
<div class="volume-channel section">
<div class="volume col-5">
<div class="row">
<button @click="volumeUp" value="volume_up">
<i class="fas fa-plus"></i>
</button>
</div>
<div class="row">
<i class="fas fa-volume-up"></i>
</div>
<div class="row">
<button @click="volumeDown" value="volume_down">
<i class="fas fa-minus"></i>
</button>
</div>
<div class="row">
<button @click="mute" value="mute">
<i class="fas fa-volume-mute"></i>
</button>
</div>
</div>
<div class="col-2">&nbsp;</div>
<div class="channel col-5">
<div class="row">
<button @click="channelUp" value="channel_up">
<i class="fas fa-plus"></i>
</button>
</div>
<div class="row">
CH
</div>
<div class="row">
<button @click="channelDown" value="channel_down">
<i class="fas fa-minus"></i>
</button>
</div>
<div class="row">
<button @click="channel" value="channel">
<i class="fas fa-keyboard"></i>
</button>
</div>
</div>
</div>
<div class="directions section">
<div class="row">
<div class="col-4">&nbsp;</div>
<div class="col-4">
<button @click="up" value="up">
<i class="fas fa-chevron-up"></i>
</button>
</div>
<div class="col-4">&nbsp;</div>
</div>
<div class="row">
<div class="col-4">
<button @click="left" value="left">
<i class="fas fa-chevron-left"></i>
</button>
</div>
<div class="col-4">
<button @click="enter" value="enter">
<i class="far fa-circle"></i>
</button>
</div>
<div class="col-4">
<button @click="right" value="right">
<i class="fas fa-chevron-right"></i>
</button>
</div>
</div>
<div class="row">
<div class="col-4">&nbsp;</div>
<div class="col-4">
<button @click="down" value="down">
<i class="fas fa-chevron-down"></i>
</button>
</div>
<div class="col-4">&nbsp;</div>
</div>
</div>
<div class="row colors section">
<div class="col-3">
<button class="color red" @click="color" value="red"></button>
</div>
<div class="col-3">
<button class="color green" @click="color" value="green"></button>
</div>
<div class="col-3">
<button class="color yellow" @click="color" value="yellow"></button>
</div>
<div class="col-3">
<button class="color blue" @click="color" value="blue"></button>
</div>
</div>
</div>
</div>
</script>

View file

@ -1,5 +1,6 @@
import os
from typing import Optional, Tuple, Union, Dict
import time
from typing import Optional, Tuple, Union, Dict, Callable, Any
from samsungtvws import SamsungTVWS
@ -52,6 +53,20 @@ class TvSamsungWsPlugin(Plugin):
return self._connections[(host, port)]
def exec(self, func: Callable[[SamsungTVWS], Any], host: Optional[str] = None, port: Optional[int] = None,
n_tries=2) -> Any:
tv = self.connect(host, port)
try:
return func(tv)
except Exception as e:
self.logger.warning(str(e))
if n_tries <= 0:
raise e
else:
time.sleep(1)
return self.exec(func, host, port, n_tries-1)
@action
def power(self, host: Optional[str] = None, port: Optional[int] = None) -> None:
"""
@ -60,8 +75,7 @@ class TvSamsungWsPlugin(Plugin):
:param host: Default host IP/name override.
:param port: Default port override.
"""
tv = self.connect(host, port)
tv.shortcuts().power()
return self.exec(lambda tv: tv.shortcuts().power(), host=host, port=port)
@action
def volume_up(self, host: Optional[str] = None, port: Optional[int] = None) -> None:
@ -71,8 +85,7 @@ class TvSamsungWsPlugin(Plugin):
:param host: Default host IP/name override.
:param port: Default port override.
"""
tv = self.connect(host, port)
tv.shortcuts().volume_up()
return self.exec(lambda tv: tv.shortcuts().volume_up(), host=host, port=port)
@action
def volume_down(self, host: Optional[str] = None, port: Optional[int] = None) -> None:
@ -82,8 +95,7 @@ class TvSamsungWsPlugin(Plugin):
:param host: Default host IP/name override.
:param port: Default port override.
"""
tv = self.connect(host, port)
tv.shortcuts().volume_down()
return self.exec(lambda tv: tv.shortcuts().volume_down(), host=host, port=port)
@action
def back(self, host: Optional[str] = None, port: Optional[int] = None) -> None:
@ -93,8 +105,7 @@ class TvSamsungWsPlugin(Plugin):
:param host: Default host IP/name override.
:param port: Default port override.
"""
tv = self.connect(host, port)
tv.shortcuts().back()
return self.exec(lambda tv: tv.shortcuts().back(), host=host, port=port)
@action
def channel(self, channel: int, host: Optional[str] = None, port: Optional[int] = None) -> None:
@ -105,8 +116,7 @@ class TvSamsungWsPlugin(Plugin):
:param host: Default host IP/name override.
:param port: Default port override.
"""
tv = self.connect(host, port)
tv.shortcuts().channel(channel)
return self.exec(lambda tv: tv.shortcuts().channel(channel), host=host, port=port)
@action
def channel_up(self, host: Optional[str] = None, port: Optional[int] = None) -> None:
@ -116,8 +126,7 @@ class TvSamsungWsPlugin(Plugin):
:param host: Default host IP/name override.
:param port: Default port override.
"""
tv = self.connect(host, port)
tv.shortcuts().channel_up()
return self.exec(lambda tv: tv.shortcuts().channel_up(), host=host, port=port)
@action
def channel_down(self, host: Optional[str] = None, port: Optional[int] = None) -> None:
@ -127,8 +136,7 @@ class TvSamsungWsPlugin(Plugin):
:param host: Default host IP/name override.
:param port: Default port override.
"""
tv = self.connect(host, port)
tv.shortcuts().channel_down()
return self.exec(lambda tv: tv.shortcuts().channel_down(), host=host, port=port)
@action
def enter(self, host: Optional[str] = None, port: Optional[int] = None) -> None:
@ -138,8 +146,7 @@ class TvSamsungWsPlugin(Plugin):
:param host: Default host IP/name override.
:param port: Default port override.
"""
tv = self.connect(host, port)
tv.shortcuts().enter()
return self.exec(lambda tv: tv.shortcuts().enter(), host=host, port=port)
@action
def guide(self, host: Optional[str] = None, port: Optional[int] = None) -> None:
@ -149,8 +156,7 @@ class TvSamsungWsPlugin(Plugin):
:param host: Default host IP/name override.
:param port: Default port override.
"""
tv = self.connect(host, port)
tv.shortcuts().guide()
return self.exec(lambda tv: tv.shortcuts().guide(), host=host, port=port)
@action
def home(self, host: Optional[str] = None, port: Optional[int] = None) -> None:
@ -160,8 +166,7 @@ class TvSamsungWsPlugin(Plugin):
:param host: Default host IP/name override.
:param port: Default port override.
"""
tv = self.connect(host, port)
tv.shortcuts().home()
return self.exec(lambda tv: tv.shortcuts().home(), host=host, port=port)
@action
def info(self, host: Optional[str] = None, port: Optional[int] = None) -> None:
@ -171,8 +176,7 @@ class TvSamsungWsPlugin(Plugin):
:param host: Default host IP/name override.
:param port: Default port override.
"""
tv = self.connect(host, port)
tv.shortcuts().info()
return self.exec(lambda tv: tv.shortcuts().info(), host=host, port=port)
@action
def menu(self, host: Optional[str] = None, port: Optional[int] = None) -> None:
@ -182,8 +186,7 @@ class TvSamsungWsPlugin(Plugin):
:param host: Default host IP/name override.
:param port: Default port override.
"""
tv = self.connect(host, port)
tv.shortcuts().menu()
return self.exec(lambda tv: tv.shortcuts().menu(), host=host, port=port)
@action
def mute(self, host: Optional[str] = None, port: Optional[int] = None) -> None:
@ -193,8 +196,7 @@ class TvSamsungWsPlugin(Plugin):
:param host: Default host IP/name override.
:param port: Default port override.
"""
tv = self.connect(host, port)
tv.shortcuts().mute()
return self.exec(lambda tv: tv.shortcuts().mute(), host=host, port=port)
@action
def source(self, host: Optional[str] = None, port: Optional[int] = None) -> None:
@ -204,8 +206,7 @@ class TvSamsungWsPlugin(Plugin):
:param host: Default host IP/name override.
:param port: Default port override.
"""
tv = self.connect(host, port)
tv.shortcuts().source()
return self.exec(lambda tv: tv.shortcuts().source(), host=host, port=port)
@action
def tools(self, host: Optional[str] = None, port: Optional[int] = None) -> None:
@ -215,8 +216,7 @@ class TvSamsungWsPlugin(Plugin):
:param host: Default host IP/name override.
:param port: Default port override.
"""
tv = self.connect(host, port)
tv.shortcuts().tools()
return self.exec(lambda tv: tv.shortcuts().tools(), host=host, port=port)
@action
def up(self, host: Optional[str] = None, port: Optional[int] = None) -> None:
@ -226,8 +226,7 @@ class TvSamsungWsPlugin(Plugin):
:param host: Default host IP/name override.
:param port: Default port override.
"""
tv = self.connect(host, port)
tv.shortcuts().up()
return self.exec(lambda tv: tv.shortcuts().up(), host=host, port=port)
@action
def down(self, host: Optional[str] = None, port: Optional[int] = None) -> None:
@ -237,8 +236,7 @@ class TvSamsungWsPlugin(Plugin):
:param host: Default host IP/name override.
:param port: Default port override.
"""
tv = self.connect(host, port)
tv.shortcuts().down()
return self.exec(lambda tv: tv.shortcuts().down(), host=host, port=port)
@action
def left(self, host: Optional[str] = None, port: Optional[int] = None) -> None:
@ -248,8 +246,7 @@ class TvSamsungWsPlugin(Plugin):
:param host: Default host IP/name override.
:param port: Default port override.
"""
tv = self.connect(host, port)
tv.shortcuts().left()
return self.exec(lambda tv: tv.shortcuts().left(), host=host, port=port)
@action
def right(self, host: Optional[str] = None, port: Optional[int] = None) -> None:
@ -259,8 +256,7 @@ class TvSamsungWsPlugin(Plugin):
:param host: Default host IP/name override.
:param port: Default port override.
"""
tv = self.connect(host, port)
tv.shortcuts().right()
return self.exec(lambda tv: tv.shortcuts().right(), host=host, port=port)
@action
def blue(self, host: Optional[str] = None, port: Optional[int] = None) -> None:
@ -270,8 +266,7 @@ class TvSamsungWsPlugin(Plugin):
:param host: Default host IP/name override.
:param port: Default port override.
"""
tv = self.connect(host, port)
tv.shortcuts().blue()
return self.exec(lambda tv: tv.shortcuts().blue(), host=host, port=port)
@action
def green(self, host: Optional[str] = None, port: Optional[int] = None) -> None:
@ -281,8 +276,7 @@ class TvSamsungWsPlugin(Plugin):
:param host: Default host IP/name override.
:param port: Default port override.
"""
tv = self.connect(host, port)
tv.shortcuts().green()
return self.exec(lambda tv: tv.shortcuts().green(), host=host, port=port)
@action
def red(self, host: Optional[str] = None, port: Optional[int] = None) -> None:
@ -292,8 +286,7 @@ class TvSamsungWsPlugin(Plugin):
:param host: Default host IP/name override.
:param port: Default port override.
"""
tv = self.connect(host, port)
tv.shortcuts().red()
return self.exec(lambda tv: tv.shortcuts().red(), host=host, port=port)
@action
def yellow(self, host: Optional[str] = None, port: Optional[int] = None) -> None:
@ -303,8 +296,7 @@ class TvSamsungWsPlugin(Plugin):
:param host: Default host IP/name override.
:param port: Default port override.
"""
tv = self.connect(host, port)
tv.shortcuts().yellow()
return self.exec(lambda tv: tv.shortcuts().yellow(), host=host, port=port)
@action
def digit(self, digit: int, host: Optional[str] = None, port: Optional[int] = None) -> None:
@ -315,8 +307,7 @@ class TvSamsungWsPlugin(Plugin):
:param host: Default host IP/name override.
:param port: Default port override.
"""
tv = self.connect(host, port)
tv.shortcuts().digit(digit)
return self.exec(lambda tv: tv.shortcuts().digit(digit), host=host, port=port)
@action
def run_app(self, app_id: Union[int, str], host: Optional[str] = None, port: Optional[int] = None) -> None:
@ -328,7 +319,43 @@ class TvSamsungWsPlugin(Plugin):
:param port: Default port override.
"""
tv = self.connect(host, port)
tv.run_app(app_id)
tv.rest_app_run(str(app_id))
@action
def close_app(self, app_id: Union[int, str], host: Optional[str] = None, port: Optional[int] = None) -> None:
"""
Close an app.
:param app_id: App ID.
:param host: Default host IP/name override.
:param port: Default port override.
"""
tv = self.connect(host, port)
tv.rest_app_close(str(app_id))
@action
def install_app(self, app_id: Union[int, str], host: Optional[str] = None, port: Optional[int] = None) -> None:
"""
Install an app.
:param app_id: App ID.
:param host: Default host IP/name override.
:param port: Default port override.
"""
tv = self.connect(host, port)
tv.rest_app_install(str(app_id))
@action
def status_app(self, app_id: Union[int, str], host: Optional[str] = None, port: Optional[int] = None) -> dict:
"""
Get the status of an app.
:param app_id: App ID.
:param host: Default host IP/name override.
:param port: Default port override.
"""
tv = self.connect(host, port)
return tv.rest_app_status(str(app_id))
@action
def list_apps(self, host: Optional[str] = None, port: Optional[int] = None) -> list:
@ -338,8 +365,7 @@ class TvSamsungWsPlugin(Plugin):
:param host: Default host IP/name override.
:param port: Default port override.
"""
tv = self.connect(host, port)
return tv.app_list()
return self.exec(lambda tv: tv.app_list(), host=host, port=port)
@action
def open_browser(self, url: str, host: Optional[str] = None, port: Optional[int] = None) -> None:
@ -350,8 +376,7 @@ class TvSamsungWsPlugin(Plugin):
:param host: Default host IP/name override.
:param port: Default port override.
"""
tv = self.connect(host, port)
return tv.open_browser(url)
return self.exec(lambda tv: tv.open_browser(url), host=host, port=port)
@action
def device_info(self, host: Optional[str] = None, port: Optional[int] = None) -> dict: