From 44671151354ec75c630084bbb00f6d7f1cb78f27 Mon Sep 17 00:00:00 2001 From: Brandon4466 Date: Fri, 11 Aug 2023 02:17:41 -0700 Subject: [PATCH] added notifications added freepbx integration as notification source --- .cache | 2 +- README.md | 4 ++-- _notify.py | 30 ++++++++++++++++++++++++++++++ freepbx.py | 24 ++++++++++++++++++++++++ phone.png | Bin 0 -> 3725 bytes phone_orig.png | Bin 0 -> 1543 bytes spotifycontroller.py | 42 +++++++++++++++++++++++------------------- update.py | 12 +++++++++++- update.zip | Bin 0 -> 6343 bytes 9 files changed, 91 insertions(+), 23 deletions(-) create mode 100644 _notify.py create mode 100644 freepbx.py create mode 100644 phone.png create mode 100644 phone_orig.png create mode 100644 update.zip diff --git a/.cache b/.cache index 46e333b..c2c3a99 100644 --- a/.cache +++ b/.cache @@ -1 +1 @@ -{"access_token": "BQALcKusPjd5akQ3U_q5i2tQANIonOR6yv-Ec9WtGBjeip5n22b82jJQRzLHhDxQI8bztKCyuqHmiiY_gA3NUqANrbqZ2ei5cG1DySXnsMIQePD__mvGikehGbfBPHJkmKTTMk2xx56jiN9Xrg2JAtjSnTxckhgT5xqxyKbpZBvQIKEsX14VIDeGgA0ZvRxmrA9n0y67bWer5Lr411k", "token_type": "Bearer", "expires_in": 3600, "scope": "user-library-modify user-library-read user-modify-playback-state user-read-playback-state", "expires_at": 1686382188, "refresh_token": "AQAby3zEGc-H8o8zciCRMZm-O6Gj3FAup6Vb0sRrbtiO48VyMTJMzU4DoJ_wrhk8LmOiN8Hvt0Fb_Ag-09XVEDgQe3VUBDD3HdoMk6aZA1n02VLygxQNMNUQgAGw6oUUdI0"} \ No newline at end of file +{"access_token": "BQDochOG-kBM3GV43moO6zgHlLbsleUy9lT5bYHi1bMBm621i-8AMWUjRDimSFAC2bUaRbh-Qv-5DA2DwwT8_hfT93s6CB1kivAESd8q7teETG2wfFFfWSv-bKvyZu5FNvDK8ofekKNy-pYwiKxeUhYuqnpMB2l0-R1ysKkkUgeqiAH3bIXrCn2HaZeya7_zrbA5uN97R38BaE22DIE", "token_type": "Bearer", "expires_in": 3600, "scope": "user-library-modify user-library-read user-modify-playback-state user-read-playback-state", "expires_at": 1689301138, "refresh_token": "AQAby3zEGc-H8o8zciCRMZm-O6Gj3FAup6Vb0sRrbtiO48VyMTJMzU4DoJ_wrhk8LmOiN8Hvt0Fb_Ag-09XVEDgQe3VUBDD3HdoMk6aZA1n02VLygxQNMNUQgAGw6oUUdI0"} \ No newline at end of file diff --git a/README.md b/README.md index 3d90060..446c3cd 100644 --- a/README.md +++ b/README.md @@ -7,11 +7,11 @@ Control Spotify remotely with a nice and functional touchscreen interface ## Running - Get a Spotify API Client ID and Secret from the [Spotify Developer Dashboard](https://developer.spotify.com/). Put these in the config.json file. -- Clone the repository +- Install required dependencies ```sh pip install -r requirements.txt ``` -- Configure the env variables +- Run ```sh python spotifycontroller.py ``` diff --git a/_notify.py b/_notify.py new file mode 100644 index 0000000..06f60b9 --- /dev/null +++ b/_notify.py @@ -0,0 +1,30 @@ +import tkinter as ttk +from tkinter import ttk as tk +from tkinter import PhotoImage +import os +import sv_ttk + +def draw(cid, num, img, title="Notification"): + root = ttk.Tk() + root.title(title) + root.geometry("1280x50") + root.attributes("-topmost", True) + if os.name == 'posix': + root.overrideredirect(1) + try: + sv_ttk.use_dark_theme() + except: + pass + + phone = PhotoImage(file=img) + phone_image = tk.Label(root, image=phone) + cid_label = tk.Label(root, text=cid, font=("Arial", 32)) + num_label = tk.Label(root, text=num, font=("Arial", 32)) + + phone_image.grid(row=0, column=0, padx=10) + cid_label.grid(row=0, column=1, padx=10) + num_label.grid(row=0, column=2, padx=10) + + root.after(15000, lambda: root.destroy()) + + root.mainloop() \ No newline at end of file diff --git a/freepbx.py b/freepbx.py new file mode 100644 index 0000000..f327ec7 --- /dev/null +++ b/freepbx.py @@ -0,0 +1,24 @@ +from flask import Flask, request +import threading +from _notify import draw + +app = Flask(__name__) + +@app.route('/hook', methods=['GET']) +def wait_for_call(): + # Get information from URL variables + num = request.args.get('num') + cid = request.args.get('CID') + img = "phone.png" + + # Process the received information (you can customize this part) + print(f"Received param1: {num}") + print(f"Received param2: {cid}") + # Add more processing logic as needed + threading.Thread(target=draw, args=(cid, num, img)).start() + + # Respond to the webhook request (optional) + return 'Webhook received successfully!', 200 + +if __name__ == '__main__': + app.run(host='0.0.0.0', port=5000) \ No newline at end of file diff --git a/phone.png b/phone.png new file mode 100644 index 0000000000000000000000000000000000000000..dc25ea82de62df2c7cc547574894e0b4cdc925d5 GIT binary patch literal 3725 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 literal 0 HcmV?d00001 diff --git a/spotifycontroller.py b/spotifycontroller.py index a3665e8..4d50427 100644 --- a/spotifycontroller.py +++ b/spotifycontroller.py @@ -210,26 +210,29 @@ def get_devices(): # unloadDevices_list() # loadSleep() # root.after(3600, get_devices) - if spotify.current_playback() != None: - count = 0 - # unloadSearching_Devices() - unloadDevices_list() - loadNow_playing() - root.after(800, update_song_label) - else: - count += 1 - if count > 420: - kill(kill=sleep) - # unloadSearching_Devices() - # loadDevices_list() + try: + if spotify.current_playback() != None: + count = 0 + # unloadSearching_Devices() + unloadDevices_list() + loadNow_playing() + root.after(800, update_song_label) else: - devices_list.delete(0, ttk.END) - list_of_devices = spotify.devices() - for num_of_device, garbage in enumerate(list_of_devices["devices"]): - # exec(f'dev_{num_of_device} = tk.Button(root, text=list_of_devices["devices"][num_of_device]["name"], command=start_playback_on_device(device_id=num_of_device))') - # exec(f'dev_{num_of_device}.grid(row={num_of_device}, column=1)') - devices_list.insert(num_of_device, list_of_devices["devices"][num_of_device]["name"]) - root.after(8500, get_devices) + count += 1 + if count > 420: + kill(kill=sleep) + # unloadSearching_Devices() + # loadDevices_list() + else: + devices_list.delete(0, ttk.END) + list_of_devices = spotify.devices() + for num_of_device, garbage in enumerate(list_of_devices["devices"]): + # exec(f'dev_{num_of_device} = tk.Button(root, text=list_of_devices["devices"][num_of_device]["name"], command=start_playback_on_device(device_id=num_of_device))') + # exec(f'dev_{num_of_device}.grid(row={num_of_device}, column=1)') + devices_list.insert(num_of_device, list_of_devices["devices"][num_of_device]["name"]) + root.after(8500, get_devices) + except: + root.after(5000, get_devices) # def wakeup(): # global count @@ -563,6 +566,7 @@ def unloadNow_playing(): song_label.grid_forget() previous_button.grid_forget() play_button.grid_forget() + pause_button.grid_forget() next_button.grid_forget() progress_bar.grid_forget() lyrics_label.grid_forget() diff --git a/update.py b/update.py index c8689de..dc5bc6a 100644 --- a/update.py +++ b/update.py @@ -6,7 +6,8 @@ from time import sleep import subprocess from pynput import mouse from functools import partial -from os import name, path, remove, system +from os import name, path, remove, system, kill, popen +import signal # while True: @@ -53,7 +54,16 @@ while True: except urllib.error.HTTPError: print("No update available.") pass + for line in popen("ps ax | grep " + "web.py" + " | grep -v grep"): + fields = line.split() + pid = fields[0] + kill(int(pid), signal.SIGKILL) subprocess.Popen(['python3', 'web/web.py']) + for line in popen("ps ax | grep " + "freepbx.py" + " | grep -v grep"): + fields = line.split() + pid = fields[0] + kill(int(pid), signal.SIGKILL) + subprocess.Popen(['python3', 'freepbx.py']) subprocess.check_call(['python3', 'spotifycontroller.py']) except Exception as e: if e.args[0] == 1: diff --git a/update.zip b/update.zip new file mode 100644 index 0000000000000000000000000000000000000000..95ec7c6d8d59e9c85e3426a0da85b26963abc7fd GIT binary patch literal 6343 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 literal 0 HcmV?d00001