From 3b1430ce9260f24a48287632023465a3eb5a33aa Mon Sep 17 00:00:00 2001 From: Bram Kelchtermans Date: Fri, 10 May 2024 23:06:08 +0200 Subject: [PATCH] volume-knob --- .../__pycache__/serial.cpython-312.pyc | Bin 0 -> 3488 bytes Python/volume-knob/config.yaml | 17 +++ Python/volume-knob/main.py | 124 ++++++++++++++++++ Python/volume-knob/requirements.txt | 3 + 4 files changed, 144 insertions(+) create mode 100644 Python/volume-knob/__pycache__/serial.cpython-312.pyc create mode 100644 Python/volume-knob/config.yaml create mode 100644 Python/volume-knob/main.py create mode 100644 Python/volume-knob/requirements.txt diff --git a/Python/volume-knob/__pycache__/serial.cpython-312.pyc b/Python/volume-knob/__pycache__/serial.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..651d3a498d9138477fa7115523a6090a251294fe GIT binary patch literal 3488 zcmb_eUrZax8K3pe+FpANHkkY)AxlDXxGCZ8O4=qzt_rsKQ!bDL(jG1nGTz0pS$mz? zbrMs2b=teeTxkI{5^2?kd$^Z2_YkR1eM~NW>I)Pk$d>9N^;9V@l}jS^l)Uttwbv#& zs=nMvp839+Z|3`E{r!I5{KeyOA!z^XJU{&d9-)6w#cH@BJS~B+f_Nk$iBV9bJaQ4v z)a?;Fj977`91%y#8F4anom2P}j$l)^E3T9~;!X(>0dz;iBRL~=5+AL%A=xdt?lNUR z5wGO_07ZO~0DXhxfxc1pOLa5t2$4fz>yVvt<6T?Xnn5tbO7+%mQ*V&HH9g1VnBz!h z+7M|1zqW{t?vQOY3(&E|*7R|-kiL#?u;b`D6YxHvj)rD`7#c?iW+}HKS!+c1!=&Dt^A(;3uaf4 zUR_I8zMN@Q=_JOweV&cIa#ff^mSx^HXZxD`n3-F3 zVG4IEYMz~Aza}rj%O0pERdXnh;WqVOwV1=!eHjFi{67mO>D%Lssuwt8Z;M^IJI9Qo z06R4687*h&J8C>Gli98l?NKeJCsJ}i6WA1niWxafs;nYB+dkiiV?z!A&xO)?d+#gL#icAd8s7~Z)%2N4Wm87D&4l4ret7)#N zBvp(8oZ*~?BWMhUoq7ao+w>K~7Q15b)7AkRO-qU~@p&kCZsIazzcv9FOwCM$)L1qp zr*&;&M9!$%M24tIIR@&boPI@3Ph3+KSnrrgt5Xvd&}4Fk8v@E^%C{oTaQGqkX@7vO zfVMsUO;5|3r{&I1);*nt-Yx99HGFgUcHhdy<%=8m*cZHalRv!1AHMV6I)AdzvxV)q z25%1DcC2`oJsY^iTyI(9Th@7Tk5%vfO>gU(w{`W{d+Xlu!T{Y4$1e@Q*4^@B?ApXl zYq)7?Y6BmHAa-ew-M+%brx5MSz^Cw};D7ot0DU*`^(yeKr2JoCuh9V|?!1jhx`XqA=QIoYKxve`hG!k3#h_zB1t$le;KWfd+an?S`JrjZgP&|RS2u=D~Z%`+ScuEY483ODkOMylc@P-@MhO*ZXr5>C}PpeRfP3D9`TpsbI z;gK-|_guIfGWhU~n4HlQYTDq;Pz-ySo(M&q!2(GViVuT}DXJzLPM|_bNu*_i1$r}l zgK}<4jS^`f4TMOt8Qoy68f+%32keI9>S*}|z(Pz03sD#hRDJDgHlZ6>g6nJ4iAjJW zp;TqyR5Xz$-vzM)e%b-3@dfnEj@->wU`PA=1pS0a$HtOCivLzRC3nk3*hqnC9D}&2}AItYfmj>7UXN%#I9SOdr&`0OD zg!;w)`TnKx4dE!22j&MB2Db(8l5e4}gzbD_t8w2-_j32I-!6u>>l>E3ev!NL#z*-r zZ{vz{*?Gr*yRLKu+kV&iIKQ>+6pHUI zI_4cON*tKIc;-c3*ul{74*oLyqQrvu;@LjrZH06Az_#F749|y`+7<>YL9RBvwkfo) z3GMU*KJZ-YgD>=n*z*>;Z*L5CqX+KJkRLsGiw~YIF9F@EIkdJ-H*+K&Q zP~fQS=ffWM;gOSJC;Mk72Xd@Rq;O{SodxXh65Z}5(Hiyc^%|+YBN&u3t9M8F5hy~g z2ggR<=?IFFtV2PQrPE?1u|xr2l>E)~H^Zl)(I>>@`cfIQv)j;3MoRetYRV8@L&aS6$!Oz^5Kz zXVLdzU&(>+p)X56vLW8TgE&Sg*=+8^h2X#Kh;tQ>fAD?N?&p0aH>^F^=nL?t_(C(f P-@O0A8TNh`1O0yihvM&V literal 0 HcmV?d00001 diff --git a/Python/volume-knob/config.yaml b/Python/volume-knob/config.yaml new file mode 100644 index 0000000..ada5c05 --- /dev/null +++ b/Python/volume-knob/config.yaml @@ -0,0 +1,17 @@ +modes: + - name: "Master" + process: "master" + - name: "Media" + process: + - "chrome.exe" + - "firefox.exe" + - "vlc.exe" + - "Spotify.exe" + - name: "Other" + process: "other" + +mqtt: + broker: "192.168.178.6" + port: 9001 + topic: "zigbee2mqtt/Desk knob" + transport: "websockets" diff --git a/Python/volume-knob/main.py b/Python/volume-knob/main.py new file mode 100644 index 0000000..e6749e0 --- /dev/null +++ b/Python/volume-knob/main.py @@ -0,0 +1,124 @@ +import paho.mqtt.client as mqtt +import json +from pycaw.pycaw import AudioUtilities, ISimpleAudioVolume, IAudioEndpointVolume +import yaml +from comtypes import CLSCTX_ALL +import time +import threading + +with open("config.yaml") as f: + try: + config = yaml.safe_load(f) + except yaml.YAMLError as exc: + print(exc) + +stopped = False + +modes = config.get("modes") +current_mode_index = 0 +# MQTT broker details +broker_address = config.get("mqtt").get("broker") +broker_port = config.get("mqtt").get("port", 1883) +topic = config.get("mqtt").get("topic") + + +def used_process_names(): + result = [] + for mode in modes: + if mode.get("process"): + if isinstance(mode.get("process"), str): + result.append(mode.get("process")) + else: + result.extend(mode.get("process")) + return result + + +def delta_volume_master(mode, delta): + devices = AudioUtilities.GetSpeakers() + interface = devices.Activate(IAudioEndpointVolume._iid_, CLSCTX_ALL, None) + volume = interface.QueryInterface(IAudioEndpointVolume) + min_volume, max_volume, step = volume.GetVolumeRange() + current_volume = volume.GetMasterVolumeLevel() + new_volume = max(min_volume, min(max_volume, current_volume + 5 * delta)) + volume.SetMasterVolumeLevel(new_volume, None) + + +def delta_volume_process(mode, delta): + is_other = mode.get("process") == "other" + process_name = mode.get("process") + if is_other: + process_names = [ + session.Process.name() + for session in AudioUtilities.GetAllSessions() + if session.Process.name() not in used_process_names() + ] + elif isinstance(process_name, str): + process_names = [process_name] + else: + process_names = process_name + sessions = AudioUtilities.GetAllSessions() + for session in sessions: + volume = session._ctl.QueryInterface(ISimpleAudioVolume) + if session.Process and session.Process.name() in process_names: + min_volume = 0 + max_volume = 1 + step = 0.2 + old_volume = volume.GetMasterVolume() + new_volume = max(min_volume, min(max_volume, old_volume + step * delta)) + volume.SetMasterVolume(new_volume, None) + + +# Function to adjust volume based on delta +def delta_volume(delta): + current_mode = modes[current_mode_index] + if current_mode == "master": + delta_volume_master(current_mode, delta) + else: + delta_volume_process(current_mode, delta) + + +previous_action = None + + +# Callback function to handle incoming messages +def on_message(client, userdata, message): + global previous_action, current_mode_index + payload = json.loads(message.payload.decode("utf-8")) + action = payload.get("action") + if action == "rotate_left": + # delta_volume(1) # Decrease volume when knob is rotated left + previous_action = action + elif action == "rotate_right": + # delta_volume(-1) # Increase volume when knob is rotated right + previous_action = action + elif action == "single": + previous_action = None + print("Switching mode to", modes[(current_mode_index + 1) % len(modes)]) + current_mode_index = (current_mode_index + 1) % len(modes) + + +def listen_for_messages(): + global previous_action + while not stopped: + time.sleep(1) + if previous_action: + delta_volume(1 if previous_action == "rotate_left" else -1) + + +transport = config.get("mqtt").get("transport", "tcp") +# Create MQTT client +client = mqtt.Client(transport=transport) +client.on_message = on_message + +# Connect to broker and subscribe to topic +client.connect(broker_address, broker_port, 60) +client.subscribe(topic) + +x = threading.Thread(target=listen_for_messages) +x.start() + + +try: + client.loop_forever() +except KeyboardInterrupt: + stopped = True diff --git a/Python/volume-knob/requirements.txt b/Python/volume-knob/requirements.txt new file mode 100644 index 0000000..4d364e4 --- /dev/null +++ b/Python/volume-knob/requirements.txt @@ -0,0 +1,3 @@ +paho-mqtt +pycaw +comtypes \ No newline at end of file