diff --git a/platypush/backend/http/static/css/source/webpanel/plugins/tv.samsung.ws/index.scss b/platypush/backend/http/static/css/source/webpanel/plugins/tv.samsung.ws/index.scss
new file mode 100644
index 000000000..aa73ed1db
--- /dev/null
+++ b/platypush/backend/http/static/css/source/webpanel/plugins/tv.samsung.ws/index.scss
@@ -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; }
+ }
+ }
+}
+
diff --git a/platypush/backend/http/static/js/plugins/tv.samsung.ws/index.js b/platypush/backend/http/static/js/plugins/tv.samsung.ws/index.js
new file mode 100644
index 000000000..b4718d01f
--- /dev/null
+++ b/platypush/backend/http/static/js/plugins/tv.samsung.ws/index.js
@@ -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);
+ }
+ },
+});
+
diff --git a/platypush/backend/http/templates/nav.html b/platypush/backend/http/templates/nav.html
index bd342acac..4ad0d55eb 100644
--- a/platypush/backend/http/templates/nav.html
+++ b/platypush/backend/http/templates/nav.html
@@ -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',
}
diff --git a/platypush/backend/http/templates/plugins/gpio/index.html b/platypush/backend/http/templates/plugins/gpio/index.html
index 457f028c1..84a5d9157 100644
--- a/platypush/backend/http/templates/plugins/gpio/index.html
+++ b/platypush/backend/http/templates/plugins/gpio/index.html
@@ -2,7 +2,7 @@
diff --git a/platypush/backend/http/templates/plugins/tv.samsung.ws/index.html b/platypush/backend/http/templates/plugins/tv.samsung.ws/index.html
new file mode 100644
index 000000000..0824208e0
--- /dev/null
+++ b/platypush/backend/http/templates/plugins/tv.samsung.ws/index.html
@@ -0,0 +1,158 @@
+
+
diff --git a/platypush/plugins/tv/samsung/ws.py b/platypush/plugins/tv/samsung/ws.py
index 33f20912c..676c668a0 100644
--- a/platypush/plugins/tv/samsung/ws.py
+++ b/platypush/plugins/tv/samsung/ws.py
@@ -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: