From 6dc6c811abe7675a0ac13b7646e47fa11b356346 Mon Sep 17 00:00:00 2001 From: Andrew Cartwright Date: Sat, 7 Dec 2019 14:00:30 -0600 Subject: [PATCH 1/5] Adding in the ability to iterate over the volume up/down call to add a little more flexibility --- roku/core.py | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/roku/core.py b/roku/core.py index b171723..c2891a5 100644 --- a/roku/core.py +++ b/roku/core.py @@ -60,6 +60,14 @@ 'poweroff': 'PowerOff', } +EXTENDED_FUNCTIONS = { + # Making the standard volume calls more iterable. + # Accepts an integer and calls the associated function by that arg times. + # roku.volume_down_by(3) + 'volume_down_by': 'VolumeDown', + 'volume_up_by': 'VolumeUp', +} + SENSORS = ('acceleration', 'magnetic', 'orientation', 'rotation') TOUCH_OPS = ('up', 'down', 'press', 'move', 'cancel') @@ -161,7 +169,7 @@ def __repr__(self): def __getattr__(self, name): - if name not in COMMANDS and name not in SENSORS: + if name not in COMMANDS and name not in SENSORS and name not in EXTENDED_FUNCTIONS: raise AttributeError('%s is not a valid method' % name) def command(*args, **kwargs): @@ -169,6 +177,10 @@ def command(*args, **kwargs): keys = ['%s.%s' % (name, axis) for axis in ('x', 'y', 'z')] params = dict(zip(keys, args)) self.input(params) + elif name in EXTENDED_FUNCTIONS: + path = '/keypress/%s' % EXTENDED_FUNCTIONS[name] + for _ in range(args[0]): + self._post(path) elif name == 'literal': for char in args[0]: path = '/keypress/%s_%s' % (COMMANDS[name], quote_plus(char)) From a6157756e8b9e9e1443056fa8a46a48a7b9fa37c Mon Sep 17 00:00:00 2001 From: Andrew Lloyd Cartwright Date: Sat, 7 Dec 2019 14:50:51 -0600 Subject: [PATCH 2/5] Update README.rst --- README.rst | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/README.rst b/README.rst index 806ed3c..811bb39 100644 --- a/README.rst +++ b/README.rst @@ -44,6 +44,19 @@ To see a full list of available commands, use the *commands* property. If you are following along on your home network and are connected to your Roku, you should see it doing stuff. *Cool!* +Extended Functions +~~~~~~~~~~~~~~~~~~ + +The library formerly had just two methods of volume control, and mute. +:: + >>> roku.volume_down() + >>> roku.volume_up() + +There are now two additional methods which iterate over the native volume methods when provided with an int parameter. +:: + >>> roku.volume_down_by(3) + >>> roku.volume_up_by(5) + Apps ~~~~ From 32bde242036f4e5d5a38c687cd2f9479e63dee48 Mon Sep 17 00:00:00 2001 From: Andrew Cartwright Date: Sat, 7 Dec 2019 15:06:34 -0600 Subject: [PATCH 3/5] Better naming conventions --- README.rst | 2 +- roku/core.py | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/README.rst b/README.rst index 811bb39..aae24f7 100644 --- a/README.rst +++ b/README.rst @@ -44,7 +44,7 @@ To see a full list of available commands, use the *commands* property. If you are following along on your home network and are connected to your Roku, you should see it doing stuff. *Cool!* -Extended Functions +Iterable Functions ~~~~~~~~~~~~~~~~~~ The library formerly had just two methods of volume control, and mute. diff --git a/roku/core.py b/roku/core.py index c2891a5..048d67a 100644 --- a/roku/core.py +++ b/roku/core.py @@ -60,7 +60,7 @@ 'poweroff': 'PowerOff', } -EXTENDED_FUNCTIONS = { +ITERABLE_FUNCTIONS = { # Making the standard volume calls more iterable. # Accepts an integer and calls the associated function by that arg times. # roku.volume_down_by(3) @@ -169,7 +169,7 @@ def __repr__(self): def __getattr__(self, name): - if name not in COMMANDS and name not in SENSORS and name not in EXTENDED_FUNCTIONS: + if name not in COMMANDS and name not in SENSORS and name not in ITERABLE_FUNCTIONS: raise AttributeError('%s is not a valid method' % name) def command(*args, **kwargs): @@ -177,8 +177,8 @@ def command(*args, **kwargs): keys = ['%s.%s' % (name, axis) for axis in ('x', 'y', 'z')] params = dict(zip(keys, args)) self.input(params) - elif name in EXTENDED_FUNCTIONS: - path = '/keypress/%s' % EXTENDED_FUNCTIONS[name] + elif name in ITERABLE_FUNCTIONS: + path = '/keypress/%s' % ITERABLE_FUNCTIONS[name] for _ in range(args[0]): self._post(path) elif name == 'literal': From 49fedc4260d508bb030147b5cec7b0663867e6fe Mon Sep 17 00:00:00 2001 From: Andrew Cartwright Date: Sun, 8 Dec 2019 22:42:33 -0600 Subject: [PATCH 4/5] Quick implementation of suggested changes for the 10 repeatable functions to allow them to take a parameter of amount of times to loop --- README.rst | 16 +++++++--------- roku/core.py | 15 ++++----------- 2 files changed, 11 insertions(+), 20 deletions(-) diff --git a/README.rst b/README.rst index aae24f7..b52398f 100644 --- a/README.rst +++ b/README.rst @@ -47,15 +47,13 @@ If you are following along on your home network and are connected to your Roku, Iterable Functions ~~~~~~~~~~~~~~~~~~ -The library formerly had just two methods of volume control, and mute. -:: - >>> roku.volume_down() - >>> roku.volume_up() - -There are now two additional methods which iterate over the native volume methods when provided with an int parameter. -:: - >>> roku.volume_down_by(3) - >>> roku.volume_up_by(5) +If there is a command that could require multiple button presses with regular physical remote usage, it can be sent multiple times in one call. + +The `volume_up`, `volume_down`, `channel_up`, `channel_down`, `backspace`, `up`, `down`, `left`, `right`, and `back` commands can all accept an integer parameter to run that command that amount of times. +:: + >>> roku.volume_down(3) + >>> roku.volume_up(5) + >>> roku.down(4) Apps ~~~~ diff --git a/roku/core.py b/roku/core.py index 048d67a..fb1d946 100644 --- a/roku/core.py +++ b/roku/core.py @@ -60,14 +60,6 @@ 'poweroff': 'PowerOff', } -ITERABLE_FUNCTIONS = { - # Making the standard volume calls more iterable. - # Accepts an integer and calls the associated function by that arg times. - # roku.volume_down_by(3) - 'volume_down_by': 'VolumeDown', - 'volume_up_by': 'VolumeUp', -} - SENSORS = ('acceleration', 'magnetic', 'orientation', 'rotation') TOUCH_OPS = ('up', 'down', 'press', 'move', 'cancel') @@ -177,9 +169,10 @@ def command(*args, **kwargs): keys = ['%s.%s' % (name, axis) for axis in ('x', 'y', 'z')] params = dict(zip(keys, args)) self.input(params) - elif name in ITERABLE_FUNCTIONS: - path = '/keypress/%s' % ITERABLE_FUNCTIONS[name] - for _ in range(args[0]): + elif name in ('volume_up', 'volume_down', 'channel_up', 'channel_down', 'backspace', 'up', 'down', 'left', 'right', 'back'): + iterations = args[0] if len(args[0]) > 0 else 1 + path = '/keypress/%s' % COMMANDS[name] + for _ in range(iterations): self._post(path) elif name == 'literal': for char in args[0]: From ac2c189b36806cd6ca07e32ef02b7c0d420cafa5 Mon Sep 17 00:00:00 2001 From: Andrew Cartwright Date: Sun, 8 Dec 2019 22:53:24 -0600 Subject: [PATCH 5/5] Moved the commands into a defined list instead of inline for readability --- roku/core.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/roku/core.py b/roku/core.py index fb1d946..e7e285c 100644 --- a/roku/core.py +++ b/roku/core.py @@ -60,6 +60,8 @@ 'poweroff': 'PowerOff', } +ITERABLE_COMMANDS = ('volume_up', 'volume_down', 'channel_up', 'channel_down', 'backspace', 'up', 'down', 'left', 'right', 'back') + SENSORS = ('acceleration', 'magnetic', 'orientation', 'rotation') TOUCH_OPS = ('up', 'down', 'press', 'move', 'cancel') @@ -161,7 +163,7 @@ def __repr__(self): def __getattr__(self, name): - if name not in COMMANDS and name not in SENSORS and name not in ITERABLE_FUNCTIONS: + if name not in COMMANDS and name not in SENSORS: raise AttributeError('%s is not a valid method' % name) def command(*args, **kwargs): @@ -169,7 +171,7 @@ def command(*args, **kwargs): keys = ['%s.%s' % (name, axis) for axis in ('x', 'y', 'z')] params = dict(zip(keys, args)) self.input(params) - elif name in ('volume_up', 'volume_down', 'channel_up', 'channel_down', 'backspace', 'up', 'down', 'left', 'right', 'back'): + elif name in ITERABLE_COMMANDS: iterations = args[0] if len(args[0]) > 0 else 1 path = '/keypress/%s' % COMMANDS[name] for _ in range(iterations):