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 0000000..651d3a4 Binary files /dev/null and b/Python/volume-knob/__pycache__/serial.cpython-312.pyc differ 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