Skip to content

Commit

Permalink
Added Settings and config storage
Browse files Browse the repository at this point in the history
  • Loading branch information
pierremtb committed Aug 24, 2019
1 parent 75d1985 commit bb585d9
Show file tree
Hide file tree
Showing 4 changed files with 148 additions and 55 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
.DS_STORE
.vscode/

# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
Expand Down
127 changes: 115 additions & 12 deletions src/app.py
Original file line number Diff line number Diff line change
@@ -1,28 +1,109 @@
import rumps
import json
import os
import keyboard
from device import Device

class StatusBarApp(rumps.App):
CONFIG_DIR = os.path.expanduser("~/.castmenu")
CONFIG_NAME = "config.json"
CONFIG_PATH = os.path.join(CONFIG_DIR, CONFIG_NAME)
DEFAULT_CONFIG = {
"DEVICE_NAME": "Desk Speaker",
"PREV_KEY": "f15",
"PLAY_KEY": "f16",
"NEXT_KEY": "f17",
"MUTE_KEY": "f18",
"VOLD_KEY": "f19",
"VOLU_KEY": "f20"
}
KEY_NAMES = {
"PREV_KEY": "Previous",
"PLAY_KEY": "Play/Pause",
"NEXT_KEY": "Next",
"MUTE_KEY": "Mute/Unmute",
"VOLD_KEY": "Volume Down",
"VOLU_KEY": "Volume Up"
}

def __init__(self, initName):
super(StatusBarApp, self).__init__(initName)
self.deviceName = initName
class CastMenuApp(rumps.App):

self.playPauseItem = rumps.MenuItem("Play", callback=lambda _ : self.__onPlayPauseClicked())
def __init__(self):
super(CastMenuApp, self).__init__("", quit_button=None)

self.config = self.getConfig()

self.chromecasts = []

self.deviceName = self.config["DEVICE_NAME"]

try:
self.device = Device(self)
self.setMenu()
self.setHotkeys()
except:
print("hey")
# self.__onDeviceSelectedClicked()

def setMenu(self):

self.playPauseItem = rumps.MenuItem("Play", callback=lambda _: self.__onPlayPauseClicked())
self.volumeDescItem = rumps.MenuItem("Volume")
self.volumeItem = rumps.SliderMenuItem(value=0, min_value=0, max_value=100, callback=lambda item : self.__onVolumeChanged(item.value))
self.volumeItem = rumps.SliderMenuItem(value=0, min_value=0, max_value=100, callback=lambda item: self.__onVolumeChanged(item.value))

self.preferencesItem = rumps.MenuItem("Preferences")
self.deviceSelectedItem = rumps.MenuItem(f"Device: {self.deviceName}", callback=lambda _ : self.__onDeviceSelectedClicked())
self.deviceSelectedItem = rumps.MenuItem(f"Device: {self.deviceName}")
# self.deviceSelectedItem = rumps.MenuItem(f"Device: {self.deviceName}", callback=lambda _ : self.__onDeviceSelectedClicked())
for cc in self.chromecasts:
name = cc.device.friendly_name
self.deviceSelectedItem.add(rumps.MenuItem(name, callback=self.__onDeviceSelected))
self.preferencesItem.add(self.deviceSelectedItem)
self.preferencesItem.add(None)
for key in ["PREV_KEY", "PLAY_KEY", "NEXT_KEY", "MUTE_KEY", "VOLD_KEY", "VOLU_KEY"]:
self.preferencesItem.add(rumps.MenuItem(f"{KEY_NAMES[key]}: {self.config[key]}", callback=self.__onKeyClicked))

self.quitItem = rumps.MenuItem("Quit", callback=lambda item: rumps.quit_application())

self.menu.clear()
self.menu = [self.playPauseItem, self.volumeDescItem, self.volumeItem, None, self.preferencesItem, self.quitItem]

self.menu = [self.playPauseItem, self.volumeDescItem, self.volumeItem, None, self.preferencesItem]
def setHotkeys(self):
keyboard.add_hotkey(self.config["PREV_KEY"], lambda: self.device.previous())
keyboard.add_hotkey(self.config["PLAY_KEY"], lambda: self.device.togglePlay())
keyboard.add_hotkey(self.config["NEXT_KEY"], lambda: self.device.next())
keyboard.add_hotkey(self.config["MUTE_KEY"], lambda: self.device.toggleMute())
keyboard.add_hotkey(self.config["VOLD_KEY"], lambda: self.device.volumeDown())
keyboard.add_hotkey(self.config["VOLU_KEY"], lambda: self.device.volumeUp())

def __onPlayPauseClicked(self):
self.device.togglePlay()

def __onDeviceSelected(self, item):
self.deviceName = item.title
self.deviceSelectedItem.title = f"Device: {self.deviceName}"
self.editConfigKey("DEVICE_NAME", self.deviceName)
self.__init__()

def __onKeyClicked(self, item):
key_name = item.title.split(": ")[0]
key = next(k for k in KEY_NAMES if KEY_NAMES[k] == key_name)
self.window = rumps.Window(f"Select key for {KEY_NAMES[key]}", title="Enter the key", default_text=self.config[key])
res = self.window.run()
self.editConfigKey(key, res.text)
self.setMenu()
self.setHotkeys()

def __onDeviceSelectedClicked(self):
self.window = rumps.Window("Enter the Chromecast-enabled device name:", title="Select Device", default_text=self.deviceName)
self.window.run()
self.deviceName = self.window.default_text
print(self.chromecasts)
availableNames = "\n".join([c.device.friendly_name for c in self.chromecasts])
message = "Available devices on network:\n" + availableNames
print(message)

self.window = rumps.Window(message, title="Enter the Chromecast-enabled device name:", default_text=self.deviceName)
res = self.window.run()
self.deviceName = res.text
self.deviceSelectedItem.title = f"Device: {self.deviceName}"
self.editConfigKey("DEVICE_NAME", self.deviceName)
self.__init__()

def __onMuteClicked(self):
self.device.toggleMute()
Expand All @@ -43,4 +124,26 @@ def updateData(self, deviceName, title, playButtonText, volume):
self.title = title
self.playPauseItem.title = playButtonText
self.volumeItem.value = volume
self.__updateVolumeDesc(volume)
self.__updateVolumeDesc(volume)

def editConfigKey(self, key, value):
self.config[key] = value
self.saveConfig(self.config)

def saveConfig(self, config):
if not os.path.exists(CONFIG_DIR):
os.makedirs(CONFIG_DIR)
with open(CONFIG_PATH, "w") as f:
json.dump(config, f)

def getConfig(self):
try:
f = open(CONFIG_PATH, "rb")
except IOError:
print("Creating config file...")
self.saveConfig(DEFAULT_CONFIG)
return DEFAULT_CONFIG

with f:
config = json.load(f)
return config
46 changes: 28 additions & 18 deletions src/device.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,31 +9,41 @@ def new_media_status(self, status):

class Device(object):

volume_offset = 0.02
volume_offset = 0.01

def __init__(self, deviceName, app):
self.deviceName = deviceName
def __init__(self, app):
self.app = app

self.connect()

self.previous_volume = self.speaker.status.volume_level
listener = StatusMediaListener(self.__onMediaChanged)
self.speaker.media_controller.register_status_listener(listener)


def connect(self):
chromecasts = pychromecast.get_chromecasts()
potentialSpeakers = [cc for cc in chromecasts if cc.device.friendly_name == self.deviceName]
self.app.title = f"Connecting to {self.app.deviceName}…"

self.app.chromecasts = pychromecast.get_chromecasts()
potentialSpeakers = [cc for cc in self.app.chromecasts if cc.device.friendly_name == self.app.deviceName]

if len(potentialSpeakers) == 0:
print("Can't find your device. Quitting...")
exit(0)
exc = f"Can't find {self.app.deviceName}"
print(exc)
raise Exception(exc)

self.speaker = potentialSpeakers[0]
self.speaker.wait()
print(self.speaker.status)
print("\nREADY!\n")
self.app.title = f"Connected to {self.deviceName}!"
self.app.title = f"Connected to {self.app.deviceName}!"

self.previous_volume = self.__getVolume()
listener = StatusMediaListener(self.__onMediaChanged)
self.speaker.media_controller.register_status_listener(listener)

def sendAction(self, action):
try:
action()
except:
self.connect()
action()

def __onMediaChanged(self, status):
volume = int(self.__getVolume() * 100)
Expand All @@ -42,10 +52,10 @@ def __onMediaChanged(self, status):
if status.player_is_paused:
title += " (:)"
playButtonText = "Play"
self.app.updateData(self.deviceName, title, playButtonText, volume)
self.app.updateData(self.app.deviceName, title, playButtonText, volume)

def __setVolume(self, volume):
self.speaker.set_volume(volume)
self.sendAction(lambda: self.speaker.set_volume(volume))
print(f"New volume: {int(volume * 100)}%")

def __getVolume(self):
Expand All @@ -69,18 +79,18 @@ def setVolume(self, volumePercent):

def togglePlay(self):
if self.speaker.media_controller.status.player_is_playing:
self.speaker.media_controller.pause()
self.sendAction(lambda: self.speaker.media_controller.pause())
self.previous_playing_state = False
print("Pause")
return
self.speaker.media_controller.play()
self.sendAction(lambda: self.speaker.media_controller.play())
self.previous_playing_state = True
print("Play")

def next(self):
self.speaker.media_controller.queue_next()
self.sendAction(lambda: self.speaker.media_controller.queue_next())
print("Next")

def previous(self):
self.speaker.media_controller.queue_prev()
self.sendAction(lambda: self.speaker.media_controller.queue_prev())
print("Previous")
27 changes: 2 additions & 25 deletions src/main.py
Original file line number Diff line number Diff line change
@@ -1,32 +1,9 @@
import keyboard
import pychromecast

from app import StatusBarApp
from device import Device

config = {
"DEVICE_NAME": "Desk Speaker",
"PREV_KEY": "f15",
"PLAY_KEY": "f16",
"NEXT_KEY": "f17",
"MUTE_KEY": "f18",
"VOLD_KEY": "f19",
"VOLU_KEY": "f20"
}
from app import CastMenuApp

if __name__ == '__main__':
app = StatusBarApp(f"Connecting to {config['DEVICE_NAME']}...")

device = Device(config["DEVICE_NAME"], app)
app.setDevice(device)

keyboard.add_hotkey(config["PREV_KEY"], lambda: device.previous())
keyboard.add_hotkey(config["PLAY_KEY"], lambda: device.togglePlay())
keyboard.add_hotkey(config["NEXT_KEY"], lambda: device.next())
keyboard.add_hotkey(config["MUTE_KEY"], lambda: device.toggleMute())
keyboard.add_hotkey(config["VOLD_KEY"], lambda: device.volumeDown())
keyboard.add_hotkey(config["VOLU_KEY"], lambda: device.volumeUp())

app = CastMenuApp()
app.run()
keyboard.wait()

0 comments on commit bb585d9

Please sign in to comment.