commit 0e23771ab14c91a8642fa97fbcfcdb7978ecfff7 Author: Daniel Weschke Date: Sat Apr 3 20:55:22 2021 +0200 inital commit diff --git a/controldeck.conf b/controldeck.conf new file mode 100644 index 0000000..4e35873 --- /dev/null +++ b/controldeck.conf @@ -0,0 +1,18 @@ +# Examples: +# +# [N.volume.NAME] +# name = sink_name +# : N. optional group/row specification +# : NAME name of the button +# : name sink name, see name with: pamixer --list-sinks +# +# [N.button.NAME] +# command = shell command +# second command +# ... +# : N. optional group/row specification +# : NAME name of the button +# : command command(s) to run + +[4.button.Test] +command = notify-send -a foo baz diff --git a/controldeck.py b/controldeck.py new file mode 100644 index 0000000..09ef675 --- /dev/null +++ b/controldeck.py @@ -0,0 +1,124 @@ +#!/usr/bin/env python +from subprocess import Popen, PIPE, STDOUT +from configparser import ConfigParser, DuplicateSectionError +from re import search, IGNORECASE +from justpy import Div, WebPage, SetRoute, justpy + +def process(args): + try: + # with shell=True args can be a string + # detached process https://stackoverflow.com/a/65900355/992129 start_new_session + # https://docs.python.org/3/library/subprocess.html#popen-constructor + result = Popen(args, stdout=PIPE, stderr=STDOUT, shell=True, start_new_session=True) + return result.stdout.read().decode("utf-8").rstrip() + except Exception as e: + print(f"{e} failed!") + +def volume(name): + return process(f'pamixer --get-volume --sink "{name}"') + +def volume_decrease(name): + return process(f'pamixer --get-volume --sink "{name}" --decrease 5') + +def volume_increase(name): + return process(f'pamixer --get-volume --sink "{name}" --increase 5') + +class Button(Div): + command = None + def __init__(self, **kwargs): + super().__init__(**kwargs) + self.classes = "bg-gray-800 hover:bg-gray-700 w-20 h-20 m-2 p-1 rounded-lg font-bold flex items-center text-center justify-center select-none" + if self.command is not None: + def click(self, msg): + print(self.command) + # string works only with shell + if isinstance(self.command, (list)): + # e.g.: [['pkill', 'ArdourGUI'], ['systemctl', '--user', 'restart', 'pipewire', 'pipewire-pulse'], ['ardour6', '-n', 'productive-pipewire']] + if isinstance(self.command[0], (list)): + [process(i) for i in self.command] + else: + # e.g.: ['pkill', 'ArdourGUI'] + process(self.command) + else: + # e.g.: 'pkill ArdourGUI' + process(self.command) + self.on('click', click) + +class ButtonSound(Div): + div = None + name = None + description = None + volume = None + + def __init__(self, **kwargs): + super().__init__(**kwargs) + self.classes = "grid-rows-2" + self.div = Div(classes="flex") + Button(inner_html=f'{self.description}
- 5%', click=self.decrease, a=self.div) + Button(inner_html=f'{self.description}
+ 5%', click=self.increase, a=self.div) + self.add(self.div) + self.volume = Div(text=f"Volume: {volume(self.name)}%", classes="text-center -mt-2", a=self) + + async def decrease(self, msg): + self.volume.text = f'Volume: {volume_decrease(self.name)}%' + + async def increase(self, msg): + self.volume.text = f'Volume: {volume_increase(self.name)}%' + +@SetRoute('/') +def application(): + wp = WebPage(title="ControlDeck", body_classes="bg-gray-900") + wp.head_html = '' + + # div = Div(classes="flex flex-wrap", a=wp) + # ButtonSound(name="Stream_sink", description="Stream sink", a=div) + # div2 = Div(classes="flex flex-wrap", a=wp) + # Button(text="Sleep", command='systemctl suspend', a=div2) + + config = ConfigParser(strict=False) + volume_dict = {} + button_dict = {} + try: + config.read('controldeck.conf') + except Exception as e: + print(f"{e}") + #print(config.sections()) + for i in config.sections(): + iname = None + iname = search("^([0-9]*.?)volume", i, flags=IGNORECASE) + if iname is not None: + id = iname.group(1)[:-1] # remove dot + try: + volume_dict[id] += [{'description': i[iname.end(0)+1:], + 'name': config.get(i, 'name', fallback=None)}] + except KeyError: + volume_dict[id] = [{'description': i[iname.end(0)+1:], + 'name': config.get(i, 'name', fallback=None)}] + iname = search("^([0-9]*.?)button", i, flags=IGNORECASE) + if iname is not None: + id = iname.group(1)[:-1] # remove dot + try: + button_dict[id] += [{'text': i[iname.end(0)+1:], + 'command': config.get(i, 'command', fallback=None)}] + except KeyError: + button_dict[id] = [{'text': i[iname.end(0)+1:], + 'command': config.get(i, 'command', fallback=None)}] + for i in volume_dict: + for j in volume_dict[i]: + if 'div'+i not in vars(): + vars()['div'+i] = Div(classes="flex flex-wrap", a=wp) + ButtonSound(name=j['name'], description=j['description'], a=eval('div'+i)) + for i in button_dict: + for j in button_dict[i]: + if 'div'+i not in vars(): + vars()['div'+i] = Div(classes="flex flex-wrap", a=wp) + Button(text=j['text'], command=j['command'], a=eval('div'+i)) + + if not wp.components: + # config not found or empty, therefore insert an empty div to not get an error + Div(text="add elements in controldeck.conf", classes="flex flex-wrap", a=wp) + + return wp + +if __name__ == '__main__': + justpy()