refactoring as gui application
:# Please enter the commit message for your changes. Lines starting
This commit is contained in:
		
							parent
							
								
									0b8f3ad9b6
								
							
						
					
					
						commit
						65f3a5c1da
					
				
							
								
								
									
										156
									
								
								__main__.py
									
									
									
									
									
								
							
							
						
						
									
										156
									
								
								__main__.py
									
									
									
									
									
								
							@ -1,155 +1,5 @@
 | 
				
			|||||||
import asyncio
 | 
					from gui import Gui
 | 
				
			||||||
DEBUG=False
 | 
					 | 
				
			||||||
try:
 | 
					 | 
				
			||||||
    from  winrt.windows.media.control import GlobalSystemMediaTransportControlsSessionManager as MediaManager
 | 
					 | 
				
			||||||
except:
 | 
					 | 
				
			||||||
    print("DEBUG; winrt disabled")
 | 
					 | 
				
			||||||
    DEBUG = True
 | 
					 | 
				
			||||||
import win32com.client
 | 
					 | 
				
			||||||
import time
 | 
					 | 
				
			||||||
import pythoncom
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
from resources import LineState, CLMgrMessage
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
import serial
 | 
					if __name__ == "__main__":
 | 
				
			||||||
from serial import SerialException
 | 
					    gui = Gui()
 | 
				
			||||||
import session_event_listener as sel
 | 
					 | 
				
			||||||
import ctypes
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
portName = 'COM3'
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
global_inactive = True
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# Lock Screen detection
 | 
					 | 
				
			||||||
user32 = ctypes.windll.User32
 | 
					 | 
				
			||||||
OpenDesktop = user32.OpenDesktopA
 | 
					 | 
				
			||||||
SwitchDesktop = user32.SwitchDesktop
 | 
					 | 
				
			||||||
DESKTOP_SWITCHDESKTOP = 0x0100
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
global_screen_locked = False
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
def check_screen_locked():
 | 
					 | 
				
			||||||
    hDesktop = OpenDesktop ("default", 0, False, DESKTOP_SWITCHDESKTOP)
 | 
					 | 
				
			||||||
    result = SwitchDesktop (hDesktop)
 | 
					 | 
				
			||||||
    if result:
 | 
					 | 
				
			||||||
       return True
 | 
					 | 
				
			||||||
    else:
 | 
					 | 
				
			||||||
       return False
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
def sendSerial(byte):
 | 
					 | 
				
			||||||
    try:
 | 
					 | 
				
			||||||
        ser = serial.Serial(port=portName, baudrate=115200)
 | 
					 | 
				
			||||||
        ser.write(byte)
 | 
					 | 
				
			||||||
    except SerialException:
 | 
					 | 
				
			||||||
        print('port already open')
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class PhoneLineEventHandler():
 | 
					 | 
				
			||||||
    phone_mgr = None
 | 
					 | 
				
			||||||
    lines = []
 | 
					 | 
				
			||||||
    line_selected = None
 | 
					 | 
				
			||||||
    connected = False
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def OnDispOnLineMgrNotification(self, msg, param, returns=""):
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        global global_inactive, global_screen_locked
 | 
					 | 
				
			||||||
        if not self.phone_mgr:
 | 
					 | 
				
			||||||
            self.phone_mgr = win32com.client.Dispatch("CLMgr.ClientLineMgr")
 | 
					 | 
				
			||||||
            if not self.phone_mgr:
 | 
					 | 
				
			||||||
                "Swyx not connected!"
 | 
					 | 
				
			||||||
            else:
 | 
					 | 
				
			||||||
                print("Swyx connected!")
 | 
					 | 
				
			||||||
        if self.phone_mgr:
 | 
					 | 
				
			||||||
            if msg == CLMgrMessage.CLMgrLineStateChangedMessage:
 | 
					 | 
				
			||||||
                line = self.phone_mgr.DispGetLine(param)
 | 
					 | 
				
			||||||
                print("Line: {} {}".format(
 | 
					 | 
				
			||||||
                    param,
 | 
					 | 
				
			||||||
                    LineState.s[line.DispState])
 | 
					 | 
				
			||||||
                )
 | 
					 | 
				
			||||||
                if line.DispState != LineState.Inactive:
 | 
					 | 
				
			||||||
                    if line.DispState == LineState.Ringing:
 | 
					 | 
				
			||||||
                        sendSerial(b'shc000255000')
 | 
					 | 
				
			||||||
                    else:
 | 
					 | 
				
			||||||
                        sendSerial(b'shc255000000')
 | 
					 | 
				
			||||||
                        
 | 
					 | 
				
			||||||
                    if not global_screen_locked:
 | 
					 | 
				
			||||||
                        asyncio.run(try_pause())
 | 
					 | 
				
			||||||
                    global_inactive = False
 | 
					 | 
				
			||||||
                for linenum in range(self.phone_mgr.DispNumberOfLines):
 | 
					 | 
				
			||||||
                    line = self.phone_mgr.DispGetLine(linenum)
 | 
					 | 
				
			||||||
                    if line.DispState != LineState.Inactive:
 | 
					 | 
				
			||||||
                        return True
 | 
					 | 
				
			||||||
                        
 | 
					 | 
				
			||||||
                if not global_screen_locked:
 | 
					 | 
				
			||||||
                    asyncio.run(try_play())
 | 
					 | 
				
			||||||
                global_inactive = True
 | 
					 | 
				
			||||||
                sendSerial(b'shc000000000')
 | 
					 | 
				
			||||||
        return True
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# async def get_media_info():
 | 
					 | 
				
			||||||
#     sessions = await MediaManager.request_async()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#     # This source_app_user_model_id check and if statement is optional
 | 
					 | 
				
			||||||
#     # Use it if you want to only get a certain player/program's media
 | 
					 | 
				
			||||||
#     # (e.g. only chrome.exe's media not any other program's).
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#     # To get the ID, use a breakpoint() to run sessions.get_current_session()
 | 
					 | 
				
			||||||
#     # while the media you want to get is playing.
 | 
					 | 
				
			||||||
#     # Then set TARGET_ID to the string this call returns.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#     current_session = sessions.get_current_session()
 | 
					 | 
				
			||||||
#     if current_session:  # there needs to be a media session running
 | 
					 | 
				
			||||||
#         #if current_session.source_app_user_model_id == TARGET_ID:
 | 
					 | 
				
			||||||
#         info = await current_session.try_get_media_properties_async()
 | 
					 | 
				
			||||||
#         # song_attr[0] != '_' ignores system attributes
 | 
					 | 
				
			||||||
#         info_dict = {song_attr: info.__getattribute__(song_attr) for song_attr in dir(info) if song_attr[0] != '_'}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#         # converts winrt vector to list
 | 
					 | 
				
			||||||
#         info_dict['genres'] = list(info_dict['genres'])
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#         return info_dict
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#     # It could be possible to select a program from a list of current
 | 
					 | 
				
			||||||
#     # available ones. I just haven't implemented this here for my use case.
 | 
					 | 
				
			||||||
#     # See references for more information.
 | 
					 | 
				
			||||||
#     raise Exception('TARGET_PROGRAM is not the current media session')
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
async def try_play():
 | 
					 | 
				
			||||||
    print("try_play")
 | 
					 | 
				
			||||||
    if not DEBUG:
 | 
					 | 
				
			||||||
        sessions = await MediaManager.request_async()
 | 
					 | 
				
			||||||
        current_session = sessions.get_current_session()
 | 
					 | 
				
			||||||
        if current_session:
 | 
					 | 
				
			||||||
            await current_session.try_play_async()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
async def try_pause():
 | 
					 | 
				
			||||||
    print("try_pause")
 | 
					 | 
				
			||||||
    if not DEBUG:
 | 
					 | 
				
			||||||
        sessions = await MediaManager.request_async()
 | 
					 | 
				
			||||||
        current_session = sessions.get_current_session()
 | 
					 | 
				
			||||||
        if current_session:
 | 
					 | 
				
			||||||
            await current_session.try_pause_async()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
def OnSessionEvent(event: sel.SessionEvent):
 | 
					 | 
				
			||||||
    global global_inactive, global_screen_locked
 | 
					 | 
				
			||||||
    if event == sel.SessionEvent.SESSION_LOCK:
 | 
					 | 
				
			||||||
        global_screen_locked = True
 | 
					 | 
				
			||||||
        print("locked, try_pause")
 | 
					 | 
				
			||||||
        asyncio.run(try_pause())
 | 
					 | 
				
			||||||
    
 | 
					 | 
				
			||||||
    if event == sel.SessionEvent.SESSION_UNLOCK:
 | 
					 | 
				
			||||||
        global_screen_locked = False
 | 
					 | 
				
			||||||
        if global_inactive:
 | 
					 | 
				
			||||||
            print("unlocked, global inactive, tryplay")
 | 
					 | 
				
			||||||
            asyncio.run(try_play())
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
if __name__ == '__main__':
 | 
					 | 
				
			||||||
    win32com.client.WithEvents("CLMgr.ClientLineMgr", PhoneLineEventHandler)
 | 
					 | 
				
			||||||
    m = sel.WorkstationMonitor()
 | 
					 | 
				
			||||||
    m.register_handler(sel.SessionEvent.ANY, handler=OnSessionEvent)
 | 
					 | 
				
			||||||
    try:
 | 
					 | 
				
			||||||
        while True:
 | 
					 | 
				
			||||||
            pythoncom.PumpWaitingMessages()
 | 
					 | 
				
			||||||
            m.listen()
 | 
					 | 
				
			||||||
            time.sleep(0.1) # Don't use up all our CPU checking constantly
 | 
					 | 
				
			||||||
    except KeyboardInterrupt:
 | 
					 | 
				
			||||||
        print("exit") 
 | 
					 | 
				
			||||||
@ -22,23 +22,19 @@ pyz = PYZ(a.pure, a.zipped_data,
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
exe = EXE(pyz,
 | 
					exe = EXE(pyz,
 | 
				
			||||||
          a.scripts,
 | 
					          a.scripts,
 | 
				
			||||||
 | 
					          a.binaries,
 | 
				
			||||||
 | 
					          a.zipfiles,
 | 
				
			||||||
 | 
					          a.datas,  
 | 
				
			||||||
          [],
 | 
					          [],
 | 
				
			||||||
          exclude_binaries=True,
 | 
					 | 
				
			||||||
          name='__main__',
 | 
					          name='__main__',
 | 
				
			||||||
          debug=False,
 | 
					          debug=False,
 | 
				
			||||||
          bootloader_ignore_signals=False,
 | 
					          bootloader_ignore_signals=False,
 | 
				
			||||||
          strip=False,
 | 
					          strip=False,
 | 
				
			||||||
          upx=True,
 | 
					          upx=True,
 | 
				
			||||||
 | 
					          upx_exclude=[],
 | 
				
			||||||
 | 
					          runtime_tmpdir=None,
 | 
				
			||||||
          console=True,
 | 
					          console=True,
 | 
				
			||||||
          disable_windowed_traceback=False,
 | 
					          disable_windowed_traceback=False,
 | 
				
			||||||
          target_arch=None,
 | 
					          target_arch=None,
 | 
				
			||||||
          codesign_identity=None,
 | 
					          codesign_identity=None,
 | 
				
			||||||
          entitlements_file=None )
 | 
					          entitlements_file=None )
 | 
				
			||||||
coll = COLLECT(exe,
 | 
					 | 
				
			||||||
               a.binaries,
 | 
					 | 
				
			||||||
               a.zipfiles,
 | 
					 | 
				
			||||||
               a.datas, 
 | 
					 | 
				
			||||||
               strip=False,
 | 
					 | 
				
			||||||
               upx=True,
 | 
					 | 
				
			||||||
               upx_exclude=[],
 | 
					 | 
				
			||||||
               name='__main__')
 | 
					 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										
											BIN
										
									
								
								favicon.ico
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								favicon.ico
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| 
		 After Width: | Height: | Size: 66 KiB  | 
							
								
								
									
										147
									
								
								gui.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										147
									
								
								gui.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,147 @@
 | 
				
			|||||||
 | 
					# Import the required libraries
 | 
				
			||||||
 | 
					import logging
 | 
				
			||||||
 | 
					from tkinter import *
 | 
				
			||||||
 | 
					from pystray import MenuItem as item
 | 
				
			||||||
 | 
					import pystray
 | 
				
			||||||
 | 
					from PIL import Image, ImageTk
 | 
				
			||||||
 | 
					import threading
 | 
				
			||||||
 | 
					from functools import partial
 | 
				
			||||||
 | 
					import time
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from mediacontrol import MediaControl
 | 
				
			||||||
 | 
					from ledcontroll import BLACK, LedControl, RED1, GREEN
 | 
				
			||||||
 | 
					from swyxcontrol import SwyxControl, LineState
 | 
				
			||||||
 | 
					from workstationcontrol import LockscreenMonitor, SessionEvent
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class Gui():
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    win: Tk
 | 
				
			||||||
 | 
					    t_icon: threading.Thread
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    strvar_swyx_connected_status: StringVar
 | 
				
			||||||
 | 
					    strvar_screen_locked: StringVar
 | 
				
			||||||
 | 
					    strvar_line_inactive: StringVar
 | 
				
			||||||
 | 
					    strvar_play_state: StringVar
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    on_quit = None
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def set_swyx_status(self, string):
 | 
				
			||||||
 | 
					        self.strvar_swyx_connected_status.set(string)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # Define a function for quit the window
 | 
				
			||||||
 | 
					    def quit_window(self, icon, item = None):
 | 
				
			||||||
 | 
					        self.sc.stop()
 | 
				
			||||||
 | 
					        self.wm.stop()
 | 
				
			||||||
 | 
					        self.mc.stop()
 | 
				
			||||||
 | 
					        icon.stop()
 | 
				
			||||||
 | 
					        self.win.deiconify()
 | 
				
			||||||
 | 
					        self.win.quit()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # Define a function to show the window again
 | 
				
			||||||
 | 
					    def show_window(self, icon, item):
 | 
				
			||||||
 | 
					        self.win.deiconify()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # Hide the window and show on the system taskbar
 | 
				
			||||||
 | 
					    def hide_window(self):
 | 
				
			||||||
 | 
					        self.win.withdraw()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def handle_lockscreen(self, event: SessionEvent):
 | 
				
			||||||
 | 
					        if event == SessionEvent.SESSION_LOCK:
 | 
				
			||||||
 | 
					            logging.debug("locked, pause")
 | 
				
			||||||
 | 
					            self.mc.pause()
 | 
				
			||||||
 | 
					            self.strvar_screen_locked.set("Locked")
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        if event == SessionEvent.SESSION_UNLOCK:
 | 
				
			||||||
 | 
					            if self.sc.all_lines_inactive:
 | 
				
			||||||
 | 
					                logging.debug("unlocked, global inactive, play")
 | 
				
			||||||
 | 
					                time.sleep(3)
 | 
				
			||||||
 | 
					                self.mc.play()
 | 
				
			||||||
 | 
					            self.strvar_screen_locked.set("Unlocked")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def handle_line_state_changed(self, line):
 | 
				
			||||||
 | 
					        if not line.DispState == LineState.Inactive:
 | 
				
			||||||
 | 
					            if line.DispState == LineState.Ringing:
 | 
				
			||||||
 | 
					                self.lc.show_all(GREEN)
 | 
				
			||||||
 | 
					            else:
 | 
				
			||||||
 | 
					                self.lc.show_all(RED1)
 | 
				
			||||||
 | 
					            self.mc.pause()
 | 
				
			||||||
 | 
					            self.strvar_play_state.set("try pause")
 | 
				
			||||||
 | 
					            self.strvar_line_inactive.set("False")
 | 
				
			||||||
 | 
					        if self.sc.all_lines_inactive:
 | 
				
			||||||
 | 
					            self.strvar_line_inactive.set("True")
 | 
				
			||||||
 | 
					            self.lc.show_all(BLACK)
 | 
				
			||||||
 | 
					            if not self.wm.screen_locked():
 | 
				
			||||||
 | 
					                self.mc.play()
 | 
				
			||||||
 | 
					                self.strvar_play_state.set("try play")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def handle_phone_mgr_connected(self):
 | 
				
			||||||
 | 
					        if self.sc.all_lines_inactive:
 | 
				
			||||||
 | 
					            self.strvar_line_inactive.set("True")
 | 
				
			||||||
 | 
					            self.lc.show_all(BLACK)
 | 
				
			||||||
 | 
					            return
 | 
				
			||||||
 | 
					        self.strvar_line_inactive.set("False")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def handle_media_control_init(self):
 | 
				
			||||||
 | 
					        playback_info = self.mc.get_playback_info()
 | 
				
			||||||
 | 
					        if playback_info is None:
 | 
				
			||||||
 | 
					            print(playback_info)
 | 
				
			||||||
 | 
					        self.strvar_play_state.set("no session active")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __init__(self):
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        self.load_gui()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.sc = SwyxControl()
 | 
				
			||||||
 | 
					        self.sc.name = "SwyxControl"
 | 
				
			||||||
 | 
					        self.sc.add_phone_mgr_connected_handler(self.handle_phone_mgr_connected)
 | 
				
			||||||
 | 
					        self.sc.add_line_state_changed_handler(self.handle_line_state_changed)
 | 
				
			||||||
 | 
					        self.sc.start()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.lc = LedControl('COM3')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.mc = MediaControl()
 | 
				
			||||||
 | 
					        self.mc.name = "MediaControl"
 | 
				
			||||||
 | 
					        self.mc.add_init_handler(self.handle_media_control_init)
 | 
				
			||||||
 | 
					        self.mc.start()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.wm = LockscreenMonitor()
 | 
				
			||||||
 | 
					        self.wm.name = "LockscreenMonitor"
 | 
				
			||||||
 | 
					        self.wm.register_handler(self.handle_lockscreen)
 | 
				
			||||||
 | 
					        self.strvar_screen_locked.set("Locked" if self.wm.screen_locked() else "Unlocked")
 | 
				
			||||||
 | 
					        self.wm.start()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.win.protocol('WM_DELETE_WINDOW', self.hide_window)
 | 
				
			||||||
 | 
					        image=Image.open("favicon.ico")
 | 
				
			||||||
 | 
					        menu=(item('Beenden', self.quit_window), item('Anzeigen', self.show_window))
 | 
				
			||||||
 | 
					        self.icon=pystray.Icon("name", image, "Swyx Media Controll", menu)
 | 
				
			||||||
 | 
					        self.t_icon = threading.Thread(target=self.icon.run)
 | 
				
			||||||
 | 
					        self.t_icon.name = "icon_thread"
 | 
				
			||||||
 | 
					        self.t_icon.start()
 | 
				
			||||||
 | 
					        self.win.mainloop()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def load_gui(self):
 | 
				
			||||||
 | 
					        self.win=Tk()
 | 
				
			||||||
 | 
					        # Create an instance of tkinter frame or window
 | 
				
			||||||
 | 
					        self.win.title("Swyx Media Controll")
 | 
				
			||||||
 | 
					        # Set the size of the window
 | 
				
			||||||
 | 
					        self.win.geometry("350x150")
 | 
				
			||||||
 | 
					        self.win.resizable(False, False)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        ## Screen Lock
 | 
				
			||||||
 | 
					        Label(self.win, text="Screen Lock: ", font=("Helvetica", 10)).place(x=10, y=10)
 | 
				
			||||||
 | 
					        self.strvar_screen_locked = StringVar(self.win)
 | 
				
			||||||
 | 
					        Label(self.win, textvariable=self.strvar_screen_locked, font=("Helvetica", 10)).place(x=100, y=10)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        ## Line Inactive
 | 
				
			||||||
 | 
					        Label(self.win, text="Line Inactive: ", font=("Helvetica", 10)).place(x=10, y=30)
 | 
				
			||||||
 | 
					        self.strvar_line_inactive = StringVar(self.win)
 | 
				
			||||||
 | 
					        Label(self.win, textvariable=self.strvar_line_inactive, font=("Helvetica", 10)).place(x=100, y=30)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        ## Play State
 | 
				
			||||||
 | 
					        Label(self.win, text="Play State: ", font=("Helvetica", 10)).place(x=10, y=50)
 | 
				
			||||||
 | 
					        self.strvar_play_state = StringVar(self.win)
 | 
				
			||||||
 | 
					        Label(self.win, textvariable=self.strvar_play_state, font=("Helvetica", 10)).place(x=100, y=50)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					if __name__ == "__main__":
 | 
				
			||||||
 | 
					    gui = Gui()
 | 
				
			||||||
							
								
								
									
										607
									
								
								ledcontroll.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										607
									
								
								ledcontroll.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,607 @@
 | 
				
			|||||||
 | 
					import serial
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class LedControl():
 | 
				
			||||||
 | 
					    portName = ''
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __init__(self, portName: str) -> None:
 | 
				
			||||||
 | 
					        '''portname is the com port of the ledstrip'''
 | 
				
			||||||
 | 
					        self.portName = portName
 | 
				
			||||||
 | 
					        pass
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def show_all(self, color:'RGB'):
 | 
				
			||||||
 | 
					        '''shows all leds in a specified color'''
 | 
				
			||||||
 | 
					        self.__sendSerial(b"shc"+color.hex_format())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def set_one(self, id, color: 'RGB'):
 | 
				
			||||||
 | 
					        '''sets a led to a specified color'''
 | 
				
			||||||
 | 
					        self.__sendSerial("dta{:03}".format(id).encode() + color.hex_format())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def show(self):
 | 
				
			||||||
 | 
					        '''shows the colors set by set_one'''
 | 
				
			||||||
 | 
					        self.__sendSerial(b'shw')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __sendSerial(self, byte):
 | 
				
			||||||
 | 
					        try:
 | 
				
			||||||
 | 
					            ser = serial.Serial(port=self.portName, baudrate=115200)
 | 
				
			||||||
 | 
					            ser.write(byte)
 | 
				
			||||||
 | 
					            for line in ser.readline():
 | 
				
			||||||
 | 
					                pass
 | 
				
			||||||
 | 
					        except serial.SerialException:
 | 
				
			||||||
 | 
					            print('port already open')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					"""Provide RGB color constants and a colors dictionary with
 | 
				
			||||||
 | 
					elements formatted: colors[colorname] = CONSTANT"""
 | 
				
			||||||
 | 
					from collections import namedtuple, OrderedDict
 | 
				
			||||||
 | 
					Color = namedtuple('RGB','red, green, blue')
 | 
				
			||||||
 | 
					colors = {} #dict of colors
 | 
				
			||||||
 | 
					class RGB(Color):
 | 
				
			||||||
 | 
					    def hex_format(self) -> bytes:
 | 
				
			||||||
 | 
					        '''Returns color in hex format'''
 | 
				
			||||||
 | 
					        return '{:03}{:03}{:03}'.format(self.red,self.green,self.blue).encode()
 | 
				
			||||||
 | 
					#Color Contants
 | 
				
			||||||
 | 
					ALICEBLUE = RGB(240, 248, 255)
 | 
				
			||||||
 | 
					ANTIQUEWHITE = RGB(250, 235, 215)
 | 
				
			||||||
 | 
					ANTIQUEWHITE1 = RGB(255, 239, 219)
 | 
				
			||||||
 | 
					ANTIQUEWHITE2 = RGB(238, 223, 204)
 | 
				
			||||||
 | 
					ANTIQUEWHITE3 = RGB(205, 192, 176)
 | 
				
			||||||
 | 
					ANTIQUEWHITE4 = RGB(139, 131, 120)
 | 
				
			||||||
 | 
					AQUA = RGB(0, 255, 255)
 | 
				
			||||||
 | 
					AQUAMARINE1 = RGB(127, 255, 212)
 | 
				
			||||||
 | 
					AQUAMARINE2 = RGB(118, 238, 198)
 | 
				
			||||||
 | 
					AQUAMARINE3 = RGB(102, 205, 170)
 | 
				
			||||||
 | 
					AQUAMARINE4 = RGB(69, 139, 116)
 | 
				
			||||||
 | 
					AZURE1 = RGB(240, 255, 255)
 | 
				
			||||||
 | 
					AZURE2 = RGB(224, 238, 238)
 | 
				
			||||||
 | 
					AZURE3 = RGB(193, 205, 205)
 | 
				
			||||||
 | 
					AZURE4 = RGB(131, 139, 139)
 | 
				
			||||||
 | 
					BANANA = RGB(227, 207, 87)
 | 
				
			||||||
 | 
					BEIGE = RGB(245, 245, 220)
 | 
				
			||||||
 | 
					BISQUE1 = RGB(255, 228, 196)
 | 
				
			||||||
 | 
					BISQUE2 = RGB(238, 213, 183)
 | 
				
			||||||
 | 
					BISQUE3 = RGB(205, 183, 158)
 | 
				
			||||||
 | 
					BISQUE4 = RGB(139, 125, 107)
 | 
				
			||||||
 | 
					BLACK = RGB(0, 0, 0)
 | 
				
			||||||
 | 
					BLANCHEDALMOND = RGB(255, 235, 205)
 | 
				
			||||||
 | 
					BLUE = RGB(0, 0, 255)
 | 
				
			||||||
 | 
					BLUE2 = RGB(0, 0, 238)
 | 
				
			||||||
 | 
					BLUE3 = RGB(0, 0, 205)
 | 
				
			||||||
 | 
					BLUE4 = RGB(0, 0, 139)
 | 
				
			||||||
 | 
					BLUEVIOLET = RGB(138, 43, 226)
 | 
				
			||||||
 | 
					BRICK = RGB(156, 102, 31)
 | 
				
			||||||
 | 
					BROWN = RGB(165, 42, 42)
 | 
				
			||||||
 | 
					BROWN1 = RGB(255, 64, 64)
 | 
				
			||||||
 | 
					BROWN2 = RGB(238, 59, 59)
 | 
				
			||||||
 | 
					BROWN3 = RGB(205, 51, 51)
 | 
				
			||||||
 | 
					BROWN4 = RGB(139, 35, 35)
 | 
				
			||||||
 | 
					BURLYWOOD = RGB(222, 184, 135)
 | 
				
			||||||
 | 
					BURLYWOOD1 = RGB(255, 211, 155)
 | 
				
			||||||
 | 
					BURLYWOOD2 = RGB(238, 197, 145)
 | 
				
			||||||
 | 
					BURLYWOOD3 = RGB(205, 170, 125)
 | 
				
			||||||
 | 
					BURLYWOOD4 = RGB(139, 115, 85)
 | 
				
			||||||
 | 
					BURNTSIENNA = RGB(138, 54, 15)
 | 
				
			||||||
 | 
					BURNTUMBER = RGB(138, 51, 36)
 | 
				
			||||||
 | 
					CADETBLUE = RGB(95, 158, 160)
 | 
				
			||||||
 | 
					CADETBLUE1 = RGB(152, 245, 255)
 | 
				
			||||||
 | 
					CADETBLUE2 = RGB(142, 229, 238)
 | 
				
			||||||
 | 
					CADETBLUE3 = RGB(122, 197, 205)
 | 
				
			||||||
 | 
					CADETBLUE4 = RGB(83, 134, 139)
 | 
				
			||||||
 | 
					CADMIUMORANGE = RGB(255, 97, 3)
 | 
				
			||||||
 | 
					CADMIUMYELLOW = RGB(255, 153, 18)
 | 
				
			||||||
 | 
					CARROT = RGB(237, 145, 33)
 | 
				
			||||||
 | 
					CHARTREUSE1 = RGB(127, 255, 0)
 | 
				
			||||||
 | 
					CHARTREUSE2 = RGB(118, 238, 0)
 | 
				
			||||||
 | 
					CHARTREUSE3 = RGB(102, 205, 0)
 | 
				
			||||||
 | 
					CHARTREUSE4 = RGB(69, 139, 0)
 | 
				
			||||||
 | 
					CHOCOLATE = RGB(210, 105, 30)
 | 
				
			||||||
 | 
					CHOCOLATE1 = RGB(255, 127, 36)
 | 
				
			||||||
 | 
					CHOCOLATE2 = RGB(238, 118, 33)
 | 
				
			||||||
 | 
					CHOCOLATE3 = RGB(205, 102, 29)
 | 
				
			||||||
 | 
					CHOCOLATE4 = RGB(139, 69, 19)
 | 
				
			||||||
 | 
					COBALT = RGB(61, 89, 171)
 | 
				
			||||||
 | 
					COBALTGREEN = RGB(61, 145, 64)
 | 
				
			||||||
 | 
					COLDGREY = RGB(128, 138, 135)
 | 
				
			||||||
 | 
					CORAL = RGB(255, 127, 80)
 | 
				
			||||||
 | 
					CORAL1 = RGB(255, 114, 86)
 | 
				
			||||||
 | 
					CORAL2 = RGB(238, 106, 80)
 | 
				
			||||||
 | 
					CORAL3 = RGB(205, 91, 69)
 | 
				
			||||||
 | 
					CORAL4 = RGB(139, 62, 47)
 | 
				
			||||||
 | 
					CORNFLOWERBLUE = RGB(100, 149, 237)
 | 
				
			||||||
 | 
					CORNSILK1 = RGB(255, 248, 220)
 | 
				
			||||||
 | 
					CORNSILK2 = RGB(238, 232, 205)
 | 
				
			||||||
 | 
					CORNSILK3 = RGB(205, 200, 177)
 | 
				
			||||||
 | 
					CORNSILK4 = RGB(139, 136, 120)
 | 
				
			||||||
 | 
					CRIMSON = RGB(220, 20, 60)
 | 
				
			||||||
 | 
					CYAN2 = RGB(0, 238, 238)
 | 
				
			||||||
 | 
					CYAN3 = RGB(0, 205, 205)
 | 
				
			||||||
 | 
					CYAN4 = RGB(0, 139, 139)
 | 
				
			||||||
 | 
					DARKGOLDENROD = RGB(184, 134, 11)
 | 
				
			||||||
 | 
					DARKGOLDENROD1 = RGB(255, 185, 15)
 | 
				
			||||||
 | 
					DARKGOLDENROD2 = RGB(238, 173, 14)
 | 
				
			||||||
 | 
					DARKGOLDENROD3 = RGB(205, 149, 12)
 | 
				
			||||||
 | 
					DARKGOLDENROD4 = RGB(139, 101, 8)
 | 
				
			||||||
 | 
					DARKGRAY = RGB(169, 169, 169)
 | 
				
			||||||
 | 
					DARKGREEN = RGB(0, 100, 0)
 | 
				
			||||||
 | 
					DARKKHAKI = RGB(189, 183, 107)
 | 
				
			||||||
 | 
					DARKOLIVEGREEN = RGB(85, 107, 47)
 | 
				
			||||||
 | 
					DARKOLIVEGREEN1 = RGB(202, 255, 112)
 | 
				
			||||||
 | 
					DARKOLIVEGREEN2 = RGB(188, 238, 104)
 | 
				
			||||||
 | 
					DARKOLIVEGREEN3 = RGB(162, 205, 90)
 | 
				
			||||||
 | 
					DARKOLIVEGREEN4 = RGB(110, 139, 61)
 | 
				
			||||||
 | 
					DARKORANGE = RGB(255, 140, 0)
 | 
				
			||||||
 | 
					DARKORANGE1 = RGB(255, 127, 0)
 | 
				
			||||||
 | 
					DARKORANGE2 = RGB(238, 118, 0)
 | 
				
			||||||
 | 
					DARKORANGE3 = RGB(205, 102, 0)
 | 
				
			||||||
 | 
					DARKORANGE4 = RGB(139, 69, 0)
 | 
				
			||||||
 | 
					DARKORCHID = RGB(153, 50, 204)
 | 
				
			||||||
 | 
					DARKORCHID1 = RGB(191, 62, 255)
 | 
				
			||||||
 | 
					DARKORCHID2 = RGB(178, 58, 238)
 | 
				
			||||||
 | 
					DARKORCHID3 = RGB(154, 50, 205)
 | 
				
			||||||
 | 
					DARKORCHID4 = RGB(104, 34, 139)
 | 
				
			||||||
 | 
					DARKSALMON = RGB(233, 150, 122)
 | 
				
			||||||
 | 
					DARKSEAGREEN = RGB(143, 188, 143)
 | 
				
			||||||
 | 
					DARKSEAGREEN1 = RGB(193, 255, 193)
 | 
				
			||||||
 | 
					DARKSEAGREEN2 = RGB(180, 238, 180)
 | 
				
			||||||
 | 
					DARKSEAGREEN3 = RGB(155, 205, 155)
 | 
				
			||||||
 | 
					DARKSEAGREEN4 = RGB(105, 139, 105)
 | 
				
			||||||
 | 
					DARKSLATEBLUE = RGB(72, 61, 139)
 | 
				
			||||||
 | 
					DARKSLATEGRAY = RGB(47, 79, 79)
 | 
				
			||||||
 | 
					DARKSLATEGRAY1 = RGB(151, 255, 255)
 | 
				
			||||||
 | 
					DARKSLATEGRAY2 = RGB(141, 238, 238)
 | 
				
			||||||
 | 
					DARKSLATEGRAY3 = RGB(121, 205, 205)
 | 
				
			||||||
 | 
					DARKSLATEGRAY4 = RGB(82, 139, 139)
 | 
				
			||||||
 | 
					DARKTURQUOISE = RGB(0, 206, 209)
 | 
				
			||||||
 | 
					DARKVIOLET = RGB(148, 0, 211)
 | 
				
			||||||
 | 
					DEEPPINK1 = RGB(255, 20, 147)
 | 
				
			||||||
 | 
					DEEPPINK2 = RGB(238, 18, 137)
 | 
				
			||||||
 | 
					DEEPPINK3 = RGB(205, 16, 118)
 | 
				
			||||||
 | 
					DEEPPINK4 = RGB(139, 10, 80)
 | 
				
			||||||
 | 
					DEEPSKYBLUE1 = RGB(0, 191, 255)
 | 
				
			||||||
 | 
					DEEPSKYBLUE2 = RGB(0, 178, 238)
 | 
				
			||||||
 | 
					DEEPSKYBLUE3 = RGB(0, 154, 205)
 | 
				
			||||||
 | 
					DEEPSKYBLUE4 = RGB(0, 104, 139)
 | 
				
			||||||
 | 
					DIMGRAY = RGB(105, 105, 105)
 | 
				
			||||||
 | 
					DIMGRAY = RGB(105, 105, 105)
 | 
				
			||||||
 | 
					DODGERBLUE1 = RGB(30, 144, 255)
 | 
				
			||||||
 | 
					DODGERBLUE2 = RGB(28, 134, 238)
 | 
				
			||||||
 | 
					DODGERBLUE3 = RGB(24, 116, 205)
 | 
				
			||||||
 | 
					DODGERBLUE4 = RGB(16, 78, 139)
 | 
				
			||||||
 | 
					EGGSHELL = RGB(252, 230, 201)
 | 
				
			||||||
 | 
					EMERALDGREEN = RGB(0, 201, 87)
 | 
				
			||||||
 | 
					FIREBRICK = RGB(178, 34, 34)
 | 
				
			||||||
 | 
					FIREBRICK1 = RGB(255, 48, 48)
 | 
				
			||||||
 | 
					FIREBRICK2 = RGB(238, 44, 44)
 | 
				
			||||||
 | 
					FIREBRICK3 = RGB(205, 38, 38)
 | 
				
			||||||
 | 
					FIREBRICK4 = RGB(139, 26, 26)
 | 
				
			||||||
 | 
					FLESH = RGB(255, 125, 64)
 | 
				
			||||||
 | 
					FLORALWHITE = RGB(255, 250, 240)
 | 
				
			||||||
 | 
					FORESTGREEN = RGB(34, 139, 34)
 | 
				
			||||||
 | 
					GAINSBORO = RGB(220, 220, 220)
 | 
				
			||||||
 | 
					GHOSTWHITE = RGB(248, 248, 255)
 | 
				
			||||||
 | 
					GOLD1 = RGB(255, 215, 0)
 | 
				
			||||||
 | 
					GOLD2 = RGB(238, 201, 0)
 | 
				
			||||||
 | 
					GOLD3 = RGB(205, 173, 0)
 | 
				
			||||||
 | 
					GOLD4 = RGB(139, 117, 0)
 | 
				
			||||||
 | 
					GOLDENROD = RGB(218, 165, 32)
 | 
				
			||||||
 | 
					GOLDENROD1 = RGB(255, 193, 37)
 | 
				
			||||||
 | 
					GOLDENROD2 = RGB(238, 180, 34)
 | 
				
			||||||
 | 
					GOLDENROD3 = RGB(205, 155, 29)
 | 
				
			||||||
 | 
					GOLDENROD4 = RGB(139, 105, 20)
 | 
				
			||||||
 | 
					GRAY = RGB(128, 128, 128)
 | 
				
			||||||
 | 
					GRAY1 = RGB(3, 3, 3)
 | 
				
			||||||
 | 
					GRAY10 = RGB(26, 26, 26)
 | 
				
			||||||
 | 
					GRAY11 = RGB(28, 28, 28)
 | 
				
			||||||
 | 
					GRAY12 = RGB(31, 31, 31)
 | 
				
			||||||
 | 
					GRAY13 = RGB(33, 33, 33)
 | 
				
			||||||
 | 
					GRAY14 = RGB(36, 36, 36)
 | 
				
			||||||
 | 
					GRAY15 = RGB(38, 38, 38)
 | 
				
			||||||
 | 
					GRAY16 = RGB(41, 41, 41)
 | 
				
			||||||
 | 
					GRAY17 = RGB(43, 43, 43)
 | 
				
			||||||
 | 
					GRAY18 = RGB(46, 46, 46)
 | 
				
			||||||
 | 
					GRAY19 = RGB(48, 48, 48)
 | 
				
			||||||
 | 
					GRAY2 = RGB(5, 5, 5)
 | 
				
			||||||
 | 
					GRAY20 = RGB(51, 51, 51)
 | 
				
			||||||
 | 
					GRAY21 = RGB(54, 54, 54)
 | 
				
			||||||
 | 
					GRAY22 = RGB(56, 56, 56)
 | 
				
			||||||
 | 
					GRAY23 = RGB(59, 59, 59)
 | 
				
			||||||
 | 
					GRAY24 = RGB(61, 61, 61)
 | 
				
			||||||
 | 
					GRAY25 = RGB(64, 64, 64)
 | 
				
			||||||
 | 
					GRAY26 = RGB(66, 66, 66)
 | 
				
			||||||
 | 
					GRAY27 = RGB(69, 69, 69)
 | 
				
			||||||
 | 
					GRAY28 = RGB(71, 71, 71)
 | 
				
			||||||
 | 
					GRAY29 = RGB(74, 74, 74)
 | 
				
			||||||
 | 
					GRAY3 = RGB(8, 8, 8)
 | 
				
			||||||
 | 
					GRAY30 = RGB(77, 77, 77)
 | 
				
			||||||
 | 
					GRAY31 = RGB(79, 79, 79)
 | 
				
			||||||
 | 
					GRAY32 = RGB(82, 82, 82)
 | 
				
			||||||
 | 
					GRAY33 = RGB(84, 84, 84)
 | 
				
			||||||
 | 
					GRAY34 = RGB(87, 87, 87)
 | 
				
			||||||
 | 
					GRAY35 = RGB(89, 89, 89)
 | 
				
			||||||
 | 
					GRAY36 = RGB(92, 92, 92)
 | 
				
			||||||
 | 
					GRAY37 = RGB(94, 94, 94)
 | 
				
			||||||
 | 
					GRAY38 = RGB(97, 97, 97)
 | 
				
			||||||
 | 
					GRAY39 = RGB(99, 99, 99)
 | 
				
			||||||
 | 
					GRAY4 = RGB(10, 10, 10)
 | 
				
			||||||
 | 
					GRAY40 = RGB(102, 102, 102)
 | 
				
			||||||
 | 
					GRAY42 = RGB(107, 107, 107)
 | 
				
			||||||
 | 
					GRAY43 = RGB(110, 110, 110)
 | 
				
			||||||
 | 
					GRAY44 = RGB(112, 112, 112)
 | 
				
			||||||
 | 
					GRAY45 = RGB(115, 115, 115)
 | 
				
			||||||
 | 
					GRAY46 = RGB(117, 117, 117)
 | 
				
			||||||
 | 
					GRAY47 = RGB(120, 120, 120)
 | 
				
			||||||
 | 
					GRAY48 = RGB(122, 122, 122)
 | 
				
			||||||
 | 
					GRAY49 = RGB(125, 125, 125)
 | 
				
			||||||
 | 
					GRAY5 = RGB(13, 13, 13)
 | 
				
			||||||
 | 
					GRAY50 = RGB(127, 127, 127)
 | 
				
			||||||
 | 
					GRAY51 = RGB(130, 130, 130)
 | 
				
			||||||
 | 
					GRAY52 = RGB(133, 133, 133)
 | 
				
			||||||
 | 
					GRAY53 = RGB(135, 135, 135)
 | 
				
			||||||
 | 
					GRAY54 = RGB(138, 138, 138)
 | 
				
			||||||
 | 
					GRAY55 = RGB(140, 140, 140)
 | 
				
			||||||
 | 
					GRAY56 = RGB(143, 143, 143)
 | 
				
			||||||
 | 
					GRAY57 = RGB(145, 145, 145)
 | 
				
			||||||
 | 
					GRAY58 = RGB(148, 148, 148)
 | 
				
			||||||
 | 
					GRAY59 = RGB(150, 150, 150)
 | 
				
			||||||
 | 
					GRAY6 = RGB(15, 15, 15)
 | 
				
			||||||
 | 
					GRAY60 = RGB(153, 153, 153)
 | 
				
			||||||
 | 
					GRAY61 = RGB(156, 156, 156)
 | 
				
			||||||
 | 
					GRAY62 = RGB(158, 158, 158)
 | 
				
			||||||
 | 
					GRAY63 = RGB(161, 161, 161)
 | 
				
			||||||
 | 
					GRAY64 = RGB(163, 163, 163)
 | 
				
			||||||
 | 
					GRAY65 = RGB(166, 166, 166)
 | 
				
			||||||
 | 
					GRAY66 = RGB(168, 168, 168)
 | 
				
			||||||
 | 
					GRAY67 = RGB(171, 171, 171)
 | 
				
			||||||
 | 
					GRAY68 = RGB(173, 173, 173)
 | 
				
			||||||
 | 
					GRAY69 = RGB(176, 176, 176)
 | 
				
			||||||
 | 
					GRAY7 = RGB(18, 18, 18)
 | 
				
			||||||
 | 
					GRAY70 = RGB(179, 179, 179)
 | 
				
			||||||
 | 
					GRAY71 = RGB(181, 181, 181)
 | 
				
			||||||
 | 
					GRAY72 = RGB(184, 184, 184)
 | 
				
			||||||
 | 
					GRAY73 = RGB(186, 186, 186)
 | 
				
			||||||
 | 
					GRAY74 = RGB(189, 189, 189)
 | 
				
			||||||
 | 
					GRAY75 = RGB(191, 191, 191)
 | 
				
			||||||
 | 
					GRAY76 = RGB(194, 194, 194)
 | 
				
			||||||
 | 
					GRAY77 = RGB(196, 196, 196)
 | 
				
			||||||
 | 
					GRAY78 = RGB(199, 199, 199)
 | 
				
			||||||
 | 
					GRAY79 = RGB(201, 201, 201)
 | 
				
			||||||
 | 
					GRAY8 = RGB(20, 20, 20)
 | 
				
			||||||
 | 
					GRAY80 = RGB(204, 204, 204)
 | 
				
			||||||
 | 
					GRAY81 = RGB(207, 207, 207)
 | 
				
			||||||
 | 
					GRAY82 = RGB(209, 209, 209)
 | 
				
			||||||
 | 
					GRAY83 = RGB(212, 212, 212)
 | 
				
			||||||
 | 
					GRAY84 = RGB(214, 214, 214)
 | 
				
			||||||
 | 
					GRAY85 = RGB(217, 217, 217)
 | 
				
			||||||
 | 
					GRAY86 = RGB(219, 219, 219)
 | 
				
			||||||
 | 
					GRAY87 = RGB(222, 222, 222)
 | 
				
			||||||
 | 
					GRAY88 = RGB(224, 224, 224)
 | 
				
			||||||
 | 
					GRAY89 = RGB(227, 227, 227)
 | 
				
			||||||
 | 
					GRAY9 = RGB(23, 23, 23)
 | 
				
			||||||
 | 
					GRAY90 = RGB(229, 229, 229)
 | 
				
			||||||
 | 
					GRAY91 = RGB(232, 232, 232)
 | 
				
			||||||
 | 
					GRAY92 = RGB(235, 235, 235)
 | 
				
			||||||
 | 
					GRAY93 = RGB(237, 237, 237)
 | 
				
			||||||
 | 
					GRAY94 = RGB(240, 240, 240)
 | 
				
			||||||
 | 
					GRAY95 = RGB(242, 242, 242)
 | 
				
			||||||
 | 
					GRAY97 = RGB(247, 247, 247)
 | 
				
			||||||
 | 
					GRAY98 = RGB(250, 250, 250)
 | 
				
			||||||
 | 
					GRAY99 = RGB(252, 252, 252)
 | 
				
			||||||
 | 
					GREEN = RGB(0, 128, 0)
 | 
				
			||||||
 | 
					GREEN1 = RGB(0, 255, 0)
 | 
				
			||||||
 | 
					GREEN2 = RGB(0, 238, 0)
 | 
				
			||||||
 | 
					GREEN3 = RGB(0, 205, 0)
 | 
				
			||||||
 | 
					GREEN4 = RGB(0, 139, 0)
 | 
				
			||||||
 | 
					GREENYELLOW = RGB(173, 255, 47)
 | 
				
			||||||
 | 
					HONEYDEW1 = RGB(240, 255, 240)
 | 
				
			||||||
 | 
					HONEYDEW2 = RGB(224, 238, 224)
 | 
				
			||||||
 | 
					HONEYDEW3 = RGB(193, 205, 193)
 | 
				
			||||||
 | 
					HONEYDEW4 = RGB(131, 139, 131)
 | 
				
			||||||
 | 
					HOTPINK = RGB(255, 105, 180)
 | 
				
			||||||
 | 
					HOTPINK1 = RGB(255, 110, 180)
 | 
				
			||||||
 | 
					HOTPINK2 = RGB(238, 106, 167)
 | 
				
			||||||
 | 
					HOTPINK3 = RGB(205, 96, 144)
 | 
				
			||||||
 | 
					HOTPINK4 = RGB(139, 58, 98)
 | 
				
			||||||
 | 
					INDIANRED = RGB(176, 23, 31)
 | 
				
			||||||
 | 
					INDIANRED = RGB(205, 92, 92)
 | 
				
			||||||
 | 
					INDIANRED1 = RGB(255, 106, 106)
 | 
				
			||||||
 | 
					INDIANRED2 = RGB(238, 99, 99)
 | 
				
			||||||
 | 
					INDIANRED3 = RGB(205, 85, 85)
 | 
				
			||||||
 | 
					INDIANRED4 = RGB(139, 58, 58)
 | 
				
			||||||
 | 
					INDIGO = RGB(75, 0, 130)
 | 
				
			||||||
 | 
					IVORY1 = RGB(255, 255, 240)
 | 
				
			||||||
 | 
					IVORY2 = RGB(238, 238, 224)
 | 
				
			||||||
 | 
					IVORY3 = RGB(205, 205, 193)
 | 
				
			||||||
 | 
					IVORY4 = RGB(139, 139, 131)
 | 
				
			||||||
 | 
					IVORYBLACK = RGB(41, 36, 33)
 | 
				
			||||||
 | 
					KHAKI = RGB(240, 230, 140)
 | 
				
			||||||
 | 
					KHAKI1 = RGB(255, 246, 143)
 | 
				
			||||||
 | 
					KHAKI2 = RGB(238, 230, 133)
 | 
				
			||||||
 | 
					KHAKI3 = RGB(205, 198, 115)
 | 
				
			||||||
 | 
					KHAKI4 = RGB(139, 134, 78)
 | 
				
			||||||
 | 
					LAVENDER = RGB(230, 230, 250)
 | 
				
			||||||
 | 
					LAVENDERBLUSH1 = RGB(255, 240, 245)
 | 
				
			||||||
 | 
					LAVENDERBLUSH2 = RGB(238, 224, 229)
 | 
				
			||||||
 | 
					LAVENDERBLUSH3 = RGB(205, 193, 197)
 | 
				
			||||||
 | 
					LAVENDERBLUSH4 = RGB(139, 131, 134)
 | 
				
			||||||
 | 
					LAWNGREEN = RGB(124, 252, 0)
 | 
				
			||||||
 | 
					LEMONCHIFFON1 = RGB(255, 250, 205)
 | 
				
			||||||
 | 
					LEMONCHIFFON2 = RGB(238, 233, 191)
 | 
				
			||||||
 | 
					LEMONCHIFFON3 = RGB(205, 201, 165)
 | 
				
			||||||
 | 
					LEMONCHIFFON4 = RGB(139, 137, 112)
 | 
				
			||||||
 | 
					LIGHTBLUE = RGB(173, 216, 230)
 | 
				
			||||||
 | 
					LIGHTBLUE1 = RGB(191, 239, 255)
 | 
				
			||||||
 | 
					LIGHTBLUE2 = RGB(178, 223, 238)
 | 
				
			||||||
 | 
					LIGHTBLUE3 = RGB(154, 192, 205)
 | 
				
			||||||
 | 
					LIGHTBLUE4 = RGB(104, 131, 139)
 | 
				
			||||||
 | 
					LIGHTCORAL = RGB(240, 128, 128)
 | 
				
			||||||
 | 
					LIGHTCYAN1 = RGB(224, 255, 255)
 | 
				
			||||||
 | 
					LIGHTCYAN2 = RGB(209, 238, 238)
 | 
				
			||||||
 | 
					LIGHTCYAN3 = RGB(180, 205, 205)
 | 
				
			||||||
 | 
					LIGHTCYAN4 = RGB(122, 139, 139)
 | 
				
			||||||
 | 
					LIGHTGOLDENROD1 = RGB(255, 236, 139)
 | 
				
			||||||
 | 
					LIGHTGOLDENROD2 = RGB(238, 220, 130)
 | 
				
			||||||
 | 
					LIGHTGOLDENROD3 = RGB(205, 190, 112)
 | 
				
			||||||
 | 
					LIGHTGOLDENROD4 = RGB(139, 129, 76)
 | 
				
			||||||
 | 
					LIGHTGOLDENRODYELLOW = RGB(250, 250, 210)
 | 
				
			||||||
 | 
					LIGHTGREY = RGB(211, 211, 211)
 | 
				
			||||||
 | 
					LIGHTPINK = RGB(255, 182, 193)
 | 
				
			||||||
 | 
					LIGHTPINK1 = RGB(255, 174, 185)
 | 
				
			||||||
 | 
					LIGHTPINK2 = RGB(238, 162, 173)
 | 
				
			||||||
 | 
					LIGHTPINK3 = RGB(205, 140, 149)
 | 
				
			||||||
 | 
					LIGHTPINK4 = RGB(139, 95, 101)
 | 
				
			||||||
 | 
					LIGHTSALMON1 = RGB(255, 160, 122)
 | 
				
			||||||
 | 
					LIGHTSALMON2 = RGB(238, 149, 114)
 | 
				
			||||||
 | 
					LIGHTSALMON3 = RGB(205, 129, 98)
 | 
				
			||||||
 | 
					LIGHTSALMON4 = RGB(139, 87, 66)
 | 
				
			||||||
 | 
					LIGHTSEAGREEN = RGB(32, 178, 170)
 | 
				
			||||||
 | 
					LIGHTSKYBLUE = RGB(135, 206, 250)
 | 
				
			||||||
 | 
					LIGHTSKYBLUE1 = RGB(176, 226, 255)
 | 
				
			||||||
 | 
					LIGHTSKYBLUE2 = RGB(164, 211, 238)
 | 
				
			||||||
 | 
					LIGHTSKYBLUE3 = RGB(141, 182, 205)
 | 
				
			||||||
 | 
					LIGHTSKYBLUE4 = RGB(96, 123, 139)
 | 
				
			||||||
 | 
					LIGHTSLATEBLUE = RGB(132, 112, 255)
 | 
				
			||||||
 | 
					LIGHTSLATEGRAY = RGB(119, 136, 153)
 | 
				
			||||||
 | 
					LIGHTSTEELBLUE = RGB(176, 196, 222)
 | 
				
			||||||
 | 
					LIGHTSTEELBLUE1 = RGB(202, 225, 255)
 | 
				
			||||||
 | 
					LIGHTSTEELBLUE2 = RGB(188, 210, 238)
 | 
				
			||||||
 | 
					LIGHTSTEELBLUE3 = RGB(162, 181, 205)
 | 
				
			||||||
 | 
					LIGHTSTEELBLUE4 = RGB(110, 123, 139)
 | 
				
			||||||
 | 
					LIGHTYELLOW1 = RGB(255, 255, 224)
 | 
				
			||||||
 | 
					LIGHTYELLOW2 = RGB(238, 238, 209)
 | 
				
			||||||
 | 
					LIGHTYELLOW3 = RGB(205, 205, 180)
 | 
				
			||||||
 | 
					LIGHTYELLOW4 = RGB(139, 139, 122)
 | 
				
			||||||
 | 
					LIMEGREEN = RGB(50, 205, 50)
 | 
				
			||||||
 | 
					LINEN = RGB(250, 240, 230)
 | 
				
			||||||
 | 
					MAGENTA = RGB(255, 0, 255)
 | 
				
			||||||
 | 
					MAGENTA2 = RGB(238, 0, 238)
 | 
				
			||||||
 | 
					MAGENTA3 = RGB(205, 0, 205)
 | 
				
			||||||
 | 
					MAGENTA4 = RGB(139, 0, 139)
 | 
				
			||||||
 | 
					MANGANESEBLUE = RGB(3, 168, 158)
 | 
				
			||||||
 | 
					MAROON = RGB(128, 0, 0)
 | 
				
			||||||
 | 
					MAROON1 = RGB(255, 52, 179)
 | 
				
			||||||
 | 
					MAROON2 = RGB(238, 48, 167)
 | 
				
			||||||
 | 
					MAROON3 = RGB(205, 41, 144)
 | 
				
			||||||
 | 
					MAROON4 = RGB(139, 28, 98)
 | 
				
			||||||
 | 
					MEDIUMORCHID = RGB(186, 85, 211)
 | 
				
			||||||
 | 
					MEDIUMORCHID1 = RGB(224, 102, 255)
 | 
				
			||||||
 | 
					MEDIUMORCHID2 = RGB(209, 95, 238)
 | 
				
			||||||
 | 
					MEDIUMORCHID3 = RGB(180, 82, 205)
 | 
				
			||||||
 | 
					MEDIUMORCHID4 = RGB(122, 55, 139)
 | 
				
			||||||
 | 
					MEDIUMPURPLE = RGB(147, 112, 219)
 | 
				
			||||||
 | 
					MEDIUMPURPLE1 = RGB(171, 130, 255)
 | 
				
			||||||
 | 
					MEDIUMPURPLE2 = RGB(159, 121, 238)
 | 
				
			||||||
 | 
					MEDIUMPURPLE3 = RGB(137, 104, 205)
 | 
				
			||||||
 | 
					MEDIUMPURPLE4 = RGB(93, 71, 139)
 | 
				
			||||||
 | 
					MEDIUMSEAGREEN = RGB(60, 179, 113)
 | 
				
			||||||
 | 
					MEDIUMSLATEBLUE = RGB(123, 104, 238)
 | 
				
			||||||
 | 
					MEDIUMSPRINGGREEN = RGB(0, 250, 154)
 | 
				
			||||||
 | 
					MEDIUMTURQUOISE = RGB(72, 209, 204)
 | 
				
			||||||
 | 
					MEDIUMVIOLETRED = RGB(199, 21, 133)
 | 
				
			||||||
 | 
					MELON = RGB(227, 168, 105)
 | 
				
			||||||
 | 
					MIDNIGHTBLUE = RGB(25, 25, 112)
 | 
				
			||||||
 | 
					MINT = RGB(189, 252, 201)
 | 
				
			||||||
 | 
					MINTCREAM = RGB(245, 255, 250)
 | 
				
			||||||
 | 
					MISTYROSE1 = RGB(255, 228, 225)
 | 
				
			||||||
 | 
					MISTYROSE2 = RGB(238, 213, 210)
 | 
				
			||||||
 | 
					MISTYROSE3 = RGB(205, 183, 181)
 | 
				
			||||||
 | 
					MISTYROSE4 = RGB(139, 125, 123)
 | 
				
			||||||
 | 
					MOCCASIN = RGB(255, 228, 181)
 | 
				
			||||||
 | 
					NAVAJOWHITE1 = RGB(255, 222, 173)
 | 
				
			||||||
 | 
					NAVAJOWHITE2 = RGB(238, 207, 161)
 | 
				
			||||||
 | 
					NAVAJOWHITE3 = RGB(205, 179, 139)
 | 
				
			||||||
 | 
					NAVAJOWHITE4 = RGB(139, 121, 94)
 | 
				
			||||||
 | 
					NAVY = RGB(0, 0, 128)
 | 
				
			||||||
 | 
					OLDLACE = RGB(253, 245, 230)
 | 
				
			||||||
 | 
					OLIVE = RGB(128, 128, 0)
 | 
				
			||||||
 | 
					OLIVEDRAB = RGB(107, 142, 35)
 | 
				
			||||||
 | 
					OLIVEDRAB1 = RGB(192, 255, 62)
 | 
				
			||||||
 | 
					OLIVEDRAB2 = RGB(179, 238, 58)
 | 
				
			||||||
 | 
					OLIVEDRAB3 = RGB(154, 205, 50)
 | 
				
			||||||
 | 
					OLIVEDRAB4 = RGB(105, 139, 34)
 | 
				
			||||||
 | 
					ORANGE = RGB(255, 128, 0)
 | 
				
			||||||
 | 
					ORANGE1 = RGB(255, 165, 0)
 | 
				
			||||||
 | 
					ORANGE2 = RGB(238, 154, 0)
 | 
				
			||||||
 | 
					ORANGE3 = RGB(205, 133, 0)
 | 
				
			||||||
 | 
					ORANGE4 = RGB(139, 90, 0)
 | 
				
			||||||
 | 
					ORANGERED1 = RGB(255, 69, 0)
 | 
				
			||||||
 | 
					ORANGERED2 = RGB(238, 64, 0)
 | 
				
			||||||
 | 
					ORANGERED3 = RGB(205, 55, 0)
 | 
				
			||||||
 | 
					ORANGERED4 = RGB(139, 37, 0)
 | 
				
			||||||
 | 
					ORCHID = RGB(218, 112, 214)
 | 
				
			||||||
 | 
					ORCHID1 = RGB(255, 131, 250)
 | 
				
			||||||
 | 
					ORCHID2 = RGB(238, 122, 233)
 | 
				
			||||||
 | 
					ORCHID3 = RGB(205, 105, 201)
 | 
				
			||||||
 | 
					ORCHID4 = RGB(139, 71, 137)
 | 
				
			||||||
 | 
					PALEGOLDENROD = RGB(238, 232, 170)
 | 
				
			||||||
 | 
					PALEGREEN = RGB(152, 251, 152)
 | 
				
			||||||
 | 
					PALEGREEN1 = RGB(154, 255, 154)
 | 
				
			||||||
 | 
					PALEGREEN2 = RGB(144, 238, 144)
 | 
				
			||||||
 | 
					PALEGREEN3 = RGB(124, 205, 124)
 | 
				
			||||||
 | 
					PALEGREEN4 = RGB(84, 139, 84)
 | 
				
			||||||
 | 
					PALETURQUOISE1 = RGB(187, 255, 255)
 | 
				
			||||||
 | 
					PALETURQUOISE2 = RGB(174, 238, 238)
 | 
				
			||||||
 | 
					PALETURQUOISE3 = RGB(150, 205, 205)
 | 
				
			||||||
 | 
					PALETURQUOISE4 = RGB(102, 139, 139)
 | 
				
			||||||
 | 
					PALEVIOLETRED = RGB(219, 112, 147)
 | 
				
			||||||
 | 
					PALEVIOLETRED1 = RGB(255, 130, 171)
 | 
				
			||||||
 | 
					PALEVIOLETRED2 = RGB(238, 121, 159)
 | 
				
			||||||
 | 
					PALEVIOLETRED3 = RGB(205, 104, 137)
 | 
				
			||||||
 | 
					PALEVIOLETRED4 = RGB(139, 71, 93)
 | 
				
			||||||
 | 
					PAPAYAWHIP = RGB(255, 239, 213)
 | 
				
			||||||
 | 
					PEACHPUFF1 = RGB(255, 218, 185)
 | 
				
			||||||
 | 
					PEACHPUFF2 = RGB(238, 203, 173)
 | 
				
			||||||
 | 
					PEACHPUFF3 = RGB(205, 175, 149)
 | 
				
			||||||
 | 
					PEACHPUFF4 = RGB(139, 119, 101)
 | 
				
			||||||
 | 
					PEACOCK = RGB(51, 161, 201)
 | 
				
			||||||
 | 
					PINK = RGB(255, 192, 203)
 | 
				
			||||||
 | 
					PINK1 = RGB(255, 181, 197)
 | 
				
			||||||
 | 
					PINK2 = RGB(238, 169, 184)
 | 
				
			||||||
 | 
					PINK3 = RGB(205, 145, 158)
 | 
				
			||||||
 | 
					PINK4 = RGB(139, 99, 108)
 | 
				
			||||||
 | 
					PLUM = RGB(221, 160, 221)
 | 
				
			||||||
 | 
					PLUM1 = RGB(255, 187, 255)
 | 
				
			||||||
 | 
					PLUM2 = RGB(238, 174, 238)
 | 
				
			||||||
 | 
					PLUM3 = RGB(205, 150, 205)
 | 
				
			||||||
 | 
					PLUM4 = RGB(139, 102, 139)
 | 
				
			||||||
 | 
					POWDERBLUE = RGB(176, 224, 230)
 | 
				
			||||||
 | 
					PURPLE = RGB(128, 0, 128)
 | 
				
			||||||
 | 
					PURPLE1 = RGB(155, 48, 255)
 | 
				
			||||||
 | 
					PURPLE2 = RGB(145, 44, 238)
 | 
				
			||||||
 | 
					PURPLE3 = RGB(125, 38, 205)
 | 
				
			||||||
 | 
					PURPLE4 = RGB(85, 26, 139)
 | 
				
			||||||
 | 
					RASPBERRY = RGB(135, 38, 87)
 | 
				
			||||||
 | 
					RAWSIENNA = RGB(199, 97, 20)
 | 
				
			||||||
 | 
					RED1 = RGB(255, 0, 0)
 | 
				
			||||||
 | 
					RED2 = RGB(238, 0, 0)
 | 
				
			||||||
 | 
					RED3 = RGB(205, 0, 0)
 | 
				
			||||||
 | 
					RED4 = RGB(139, 0, 0)
 | 
				
			||||||
 | 
					ROSYBROWN = RGB(188, 143, 143)
 | 
				
			||||||
 | 
					ROSYBROWN1 = RGB(255, 193, 193)
 | 
				
			||||||
 | 
					ROSYBROWN2 = RGB(238, 180, 180)
 | 
				
			||||||
 | 
					ROSYBROWN3 = RGB(205, 155, 155)
 | 
				
			||||||
 | 
					ROSYBROWN4 = RGB(139, 105, 105)
 | 
				
			||||||
 | 
					ROYALBLUE = RGB(65, 105, 225)
 | 
				
			||||||
 | 
					ROYALBLUE1 = RGB(72, 118, 255)
 | 
				
			||||||
 | 
					ROYALBLUE2 = RGB(67, 110, 238)
 | 
				
			||||||
 | 
					ROYALBLUE3 = RGB(58, 95, 205)
 | 
				
			||||||
 | 
					ROYALBLUE4 = RGB(39, 64, 139)
 | 
				
			||||||
 | 
					SALMON = RGB(250, 128, 114)
 | 
				
			||||||
 | 
					SALMON1 = RGB(255, 140, 105)
 | 
				
			||||||
 | 
					SALMON2 = RGB(238, 130, 98)
 | 
				
			||||||
 | 
					SALMON3 = RGB(205, 112, 84)
 | 
				
			||||||
 | 
					SALMON4 = RGB(139, 76, 57)
 | 
				
			||||||
 | 
					SANDYBROWN = RGB(244, 164, 96)
 | 
				
			||||||
 | 
					SAPGREEN = RGB(48, 128, 20)
 | 
				
			||||||
 | 
					SEAGREEN1 = RGB(84, 255, 159)
 | 
				
			||||||
 | 
					SEAGREEN2 = RGB(78, 238, 148)
 | 
				
			||||||
 | 
					SEAGREEN3 = RGB(67, 205, 128)
 | 
				
			||||||
 | 
					SEAGREEN4 = RGB(46, 139, 87)
 | 
				
			||||||
 | 
					SEASHELL1 = RGB(255, 245, 238)
 | 
				
			||||||
 | 
					SEASHELL2 = RGB(238, 229, 222)
 | 
				
			||||||
 | 
					SEASHELL3 = RGB(205, 197, 191)
 | 
				
			||||||
 | 
					SEASHELL4 = RGB(139, 134, 130)
 | 
				
			||||||
 | 
					SEPIA = RGB(94, 38, 18)
 | 
				
			||||||
 | 
					SGIBEET = RGB(142, 56, 142)
 | 
				
			||||||
 | 
					SGIBRIGHTGRAY = RGB(197, 193, 170)
 | 
				
			||||||
 | 
					SGICHARTREUSE = RGB(113, 198, 113)
 | 
				
			||||||
 | 
					SGIDARKGRAY = RGB(85, 85, 85)
 | 
				
			||||||
 | 
					SGIGRAY12 = RGB(30, 30, 30)
 | 
				
			||||||
 | 
					SGIGRAY16 = RGB(40, 40, 40)
 | 
				
			||||||
 | 
					SGIGRAY32 = RGB(81, 81, 81)
 | 
				
			||||||
 | 
					SGIGRAY36 = RGB(91, 91, 91)
 | 
				
			||||||
 | 
					SGIGRAY52 = RGB(132, 132, 132)
 | 
				
			||||||
 | 
					SGIGRAY56 = RGB(142, 142, 142)
 | 
				
			||||||
 | 
					SGIGRAY72 = RGB(183, 183, 183)
 | 
				
			||||||
 | 
					SGIGRAY76 = RGB(193, 193, 193)
 | 
				
			||||||
 | 
					SGIGRAY92 = RGB(234, 234, 234)
 | 
				
			||||||
 | 
					SGIGRAY96 = RGB(244, 244, 244)
 | 
				
			||||||
 | 
					SGILIGHTBLUE = RGB(125, 158, 192)
 | 
				
			||||||
 | 
					SGILIGHTGRAY = RGB(170, 170, 170)
 | 
				
			||||||
 | 
					SGIOLIVEDRAB = RGB(142, 142, 56)
 | 
				
			||||||
 | 
					SGISALMON = RGB(198, 113, 113)
 | 
				
			||||||
 | 
					SGISLATEBLUE = RGB(113, 113, 198)
 | 
				
			||||||
 | 
					SGITEAL = RGB(56, 142, 142)
 | 
				
			||||||
 | 
					SIENNA = RGB(160, 82, 45)
 | 
				
			||||||
 | 
					SIENNA1 = RGB(255, 130, 71)
 | 
				
			||||||
 | 
					SIENNA2 = RGB(238, 121, 66)
 | 
				
			||||||
 | 
					SIENNA3 = RGB(205, 104, 57)
 | 
				
			||||||
 | 
					SIENNA4 = RGB(139, 71, 38)
 | 
				
			||||||
 | 
					SILVER = RGB(192, 192, 192)
 | 
				
			||||||
 | 
					SKYBLUE = RGB(135, 206, 235)
 | 
				
			||||||
 | 
					SKYBLUE1 = RGB(135, 206, 255)
 | 
				
			||||||
 | 
					SKYBLUE2 = RGB(126, 192, 238)
 | 
				
			||||||
 | 
					SKYBLUE3 = RGB(108, 166, 205)
 | 
				
			||||||
 | 
					SKYBLUE4 = RGB(74, 112, 139)
 | 
				
			||||||
 | 
					SLATEBLUE = RGB(106, 90, 205)
 | 
				
			||||||
 | 
					SLATEBLUE1 = RGB(131, 111, 255)
 | 
				
			||||||
 | 
					SLATEBLUE2 = RGB(122, 103, 238)
 | 
				
			||||||
 | 
					SLATEBLUE3 = RGB(105, 89, 205)
 | 
				
			||||||
 | 
					SLATEBLUE4 = RGB(71, 60, 139)
 | 
				
			||||||
 | 
					SLATEGRAY = RGB(112, 128, 144)
 | 
				
			||||||
 | 
					SLATEGRAY1 = RGB(198, 226, 255)
 | 
				
			||||||
 | 
					SLATEGRAY2 = RGB(185, 211, 238)
 | 
				
			||||||
 | 
					SLATEGRAY3 = RGB(159, 182, 205)
 | 
				
			||||||
 | 
					SLATEGRAY4 = RGB(108, 123, 139)
 | 
				
			||||||
 | 
					SNOW1 = RGB(255, 250, 250)
 | 
				
			||||||
 | 
					SNOW2 = RGB(238, 233, 233)
 | 
				
			||||||
 | 
					SNOW3 = RGB(205, 201, 201)
 | 
				
			||||||
 | 
					SNOW4 = RGB(139, 137, 137)
 | 
				
			||||||
 | 
					SPRINGGREEN = RGB(0, 255, 127)
 | 
				
			||||||
 | 
					SPRINGGREEN1 = RGB(0, 238, 118)
 | 
				
			||||||
 | 
					SPRINGGREEN2 = RGB(0, 205, 102)
 | 
				
			||||||
 | 
					SPRINGGREEN3 = RGB(0, 139, 69)
 | 
				
			||||||
 | 
					STEELBLUE = RGB(70, 130, 180)
 | 
				
			||||||
 | 
					STEELBLUE1 = RGB(99, 184, 255)
 | 
				
			||||||
 | 
					STEELBLUE2 = RGB(92, 172, 238)
 | 
				
			||||||
 | 
					STEELBLUE3 = RGB(79, 148, 205)
 | 
				
			||||||
 | 
					STEELBLUE4 = RGB(54, 100, 139)
 | 
				
			||||||
 | 
					TAN = RGB(210, 180, 140)
 | 
				
			||||||
 | 
					TAN1 = RGB(255, 165, 79)
 | 
				
			||||||
 | 
					TAN2 = RGB(238, 154, 73)
 | 
				
			||||||
 | 
					TAN3 = RGB(205, 133, 63)
 | 
				
			||||||
 | 
					TAN4 = RGB(139, 90, 43)
 | 
				
			||||||
 | 
					TEAL = RGB(0, 128, 128)
 | 
				
			||||||
 | 
					THISTLE = RGB(216, 191, 216)
 | 
				
			||||||
 | 
					THISTLE1 = RGB(255, 225, 255)
 | 
				
			||||||
 | 
					THISTLE2 = RGB(238, 210, 238)
 | 
				
			||||||
 | 
					THISTLE3 = RGB(205, 181, 205)
 | 
				
			||||||
 | 
					THISTLE4 = RGB(139, 123, 139)
 | 
				
			||||||
 | 
					TOMATO1 = RGB(255, 99, 71)
 | 
				
			||||||
 | 
					TOMATO2 = RGB(238, 92, 66)
 | 
				
			||||||
 | 
					TOMATO3 = RGB(205, 79, 57)
 | 
				
			||||||
 | 
					TOMATO4 = RGB(139, 54, 38)
 | 
				
			||||||
 | 
					TURQUOISE = RGB(64, 224, 208)
 | 
				
			||||||
 | 
					TURQUOISE1 = RGB(0, 245, 255)
 | 
				
			||||||
 | 
					TURQUOISE2 = RGB(0, 229, 238)
 | 
				
			||||||
 | 
					TURQUOISE3 = RGB(0, 197, 205)
 | 
				
			||||||
 | 
					TURQUOISE4 = RGB(0, 134, 139)
 | 
				
			||||||
 | 
					TURQUOISEBLUE = RGB(0, 199, 140)
 | 
				
			||||||
 | 
					VIOLET = RGB(238, 130, 238)
 | 
				
			||||||
 | 
					VIOLETRED = RGB(208, 32, 144)
 | 
				
			||||||
 | 
					VIOLETRED1 = RGB(255, 62, 150)
 | 
				
			||||||
 | 
					VIOLETRED2 = RGB(238, 58, 140)
 | 
				
			||||||
 | 
					VIOLETRED3 = RGB(205, 50, 120)
 | 
				
			||||||
 | 
					VIOLETRED4 = RGB(139, 34, 82)
 | 
				
			||||||
 | 
					WARMGREY = RGB(128, 128, 105)
 | 
				
			||||||
 | 
					WHEAT = RGB(245, 222, 179)
 | 
				
			||||||
 | 
					WHEAT1 = RGB(255, 231, 186)
 | 
				
			||||||
 | 
					WHEAT2 = RGB(238, 216, 174)
 | 
				
			||||||
 | 
					WHEAT3 = RGB(205, 186, 150)
 | 
				
			||||||
 | 
					WHEAT4 = RGB(139, 126, 102)
 | 
				
			||||||
 | 
					WHITE = RGB(255, 255, 255)
 | 
				
			||||||
 | 
					WHITESMOKE = RGB(245, 245, 245)
 | 
				
			||||||
 | 
					WHITESMOKE = RGB(245, 245, 245)
 | 
				
			||||||
 | 
					YELLOW1 = RGB(255, 255, 0)
 | 
				
			||||||
 | 
					YELLOW2 = RGB(238, 238, 0)
 | 
				
			||||||
 | 
					YELLOW3 = RGB(205, 205, 0)
 | 
				
			||||||
 | 
					YELLOW4 = RGB(139, 139, 0)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					if __name__ == "__main__":
 | 
				
			||||||
 | 
					    import time
 | 
				
			||||||
 | 
					    ledcount = 9
 | 
				
			||||||
 | 
					    lc = LedControl('COM3')
 | 
				
			||||||
 | 
					    while(True):
 | 
				
			||||||
 | 
					        for led in range(ledcount):
 | 
				
			||||||
 | 
					            lc.set_one(led, BLUEVIOLET)
 | 
				
			||||||
 | 
					            lc.show()
 | 
				
			||||||
 | 
					            time.sleep(0.1)
 | 
				
			||||||
 | 
					        lc.show_all(BLACK)
 | 
				
			||||||
 | 
					        time.sleep(0.1)
 | 
				
			||||||
							
								
								
									
										119
									
								
								mediacontrol.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										119
									
								
								mediacontrol.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,119 @@
 | 
				
			|||||||
 | 
					import asyncio
 | 
				
			||||||
 | 
					from threading import Thread
 | 
				
			||||||
 | 
					import logging
 | 
				
			||||||
 | 
					import time
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class SessionPlaybackStatus:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Changing: 2 # Das Medium ändert sich.
 | 
				
			||||||
 | 
					    Closed:	0	# Das Medium ist geschlossen.
 | 
				
			||||||
 | 
					    Opened:	1   # Das Medium wird geöffnet.
 | 
				
			||||||
 | 
					    Paused:	5   # Das Medium wird angehalten.
 | 
				
			||||||
 | 
					    Playing: 4	# Die Medien werden abspielt.
 | 
				
			||||||
 | 
					    Stopped: 3  # Das Medium wird beendet.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class MediaControl(Thread):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    __init_handler = []
 | 
				
			||||||
 | 
					    __session_changed_handler = []
 | 
				
			||||||
 | 
					    __running = True
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					    def run(self) -> None:
 | 
				
			||||||
 | 
					        from  winrt.windows.media.control import GlobalSystemMediaTransportControlsSessionManager as MediaManager, CurrentSessionChangedEventArgs
 | 
				
			||||||
 | 
					       
 | 
				
			||||||
 | 
					        self.MediaManager = MediaManager
 | 
				
			||||||
 | 
					        # MediaManager.add_current_session_changed(Event)
 | 
				
			||||||
 | 
					        for handler in self.__init_handler:
 | 
				
			||||||
 | 
					            handler()
 | 
				
			||||||
 | 
					        while self.__running:
 | 
				
			||||||
 | 
					            time.sleep(1)
 | 
				
			||||||
 | 
					        return super().run()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __init__(self) -> None:
 | 
				
			||||||
 | 
					        super().__init__()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __current_session_changed(self, **kwargs):
 | 
				
			||||||
 | 
					        print(kwargs)
 | 
				
			||||||
 | 
					        for handler in self.__session_changed_handler:
 | 
				
			||||||
 | 
					            handler()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def add_init_handler(self, handler):
 | 
				
			||||||
 | 
					        self.__init_handler.append(handler)
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    # def add_session_changed_handler(self, handler):
 | 
				
			||||||
 | 
					    #     self.__session_changed_handler.append(handler)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def play(self):
 | 
				
			||||||
 | 
					        asyncio.run(self.__try_play())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def pause(self):
 | 
				
			||||||
 | 
					        asyncio.run(self.__try_pause())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def get_media_info(self):
 | 
				
			||||||
 | 
					        return asyncio.run(self.__get_media_info())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def get_playback_info(self):
 | 
				
			||||||
 | 
					        return asyncio.run(self.__get_playback_info())
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    def stop(self):
 | 
				
			||||||
 | 
					        self.__running = False
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    async def __try_play(self):
 | 
				
			||||||
 | 
					        logging.debug("__try_play")
 | 
				
			||||||
 | 
					        sessions = await self.MediaManager.request_async()
 | 
				
			||||||
 | 
					        current_session = sessions.get_current_session()
 | 
				
			||||||
 | 
					        if current_session:
 | 
				
			||||||
 | 
					            print("try_play!!!!!!!")
 | 
				
			||||||
 | 
					            await current_session.try_play_async()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    async def __try_pause(self):
 | 
				
			||||||
 | 
					        logging.debug("__try_pause")
 | 
				
			||||||
 | 
					        sessions = await self.MediaManager.request_async()
 | 
				
			||||||
 | 
					        current_session = sessions.get_current_session()
 | 
				
			||||||
 | 
					        if current_session:
 | 
				
			||||||
 | 
					            await current_session.try_pause_async()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    async def __get_media_info(self):
 | 
				
			||||||
 | 
					        sessions = await self.MediaManager.request_async()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        current_session = sessions.get_current_session()
 | 
				
			||||||
 | 
					        if current_session:  # there needs to be a media session running
 | 
				
			||||||
 | 
					            info = await current_session.try_get_media_properties_async()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            # song_attr[0] != '_' ignores system attributes
 | 
				
			||||||
 | 
					            info_dict = {song_attr: info.__getattribute__(song_attr) for song_attr in dir(info) if song_attr[0] != '_'}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            # converts winrt vector to list
 | 
				
			||||||
 | 
					            info_dict['genres'] = list(info_dict['genres'])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            return info_dict
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    async def __get_playback_info(self):
 | 
				
			||||||
 | 
					        sessions = await self.MediaManager.request_async()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        current_session = sessions.get_current_session()
 | 
				
			||||||
 | 
					        if current_session:  # there needs to be a media session running
 | 
				
			||||||
 | 
					            info = current_session.get_playback_info()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            # song_attr[0] != '_' ignores system attributes
 | 
				
			||||||
 | 
					            info_dict = {playback_attr: info.__getattribute__(playback_attr) for playback_attr in dir(info) if playback_attr[0] != '_'}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            # converts winrt vector to list
 | 
				
			||||||
 | 
					            info_dict['controls'] = {control_attr: info.controls.__getattribute__(control_attr) for control_attr in dir(info.controls) if control_attr[0] != '_'}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            return info_dict
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					if __name__ == "__main__":
 | 
				
			||||||
 | 
					    def handle_init():
 | 
				
			||||||
 | 
					        print(mc.get_media_info())
 | 
				
			||||||
 | 
					        print(mc.get_playback_info())
 | 
				
			||||||
 | 
					        pass
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    logging.getLogger().setLevel(logging.DEBUG)
 | 
				
			||||||
 | 
					    mc = MediaControl()
 | 
				
			||||||
 | 
					    mc.start()
 | 
				
			||||||
 | 
					    mc.add_init_handler(handle_init)
 | 
				
			||||||
@ -1,3 +1,4 @@
 | 
				
			|||||||
winrt
 | 
					winrt
 | 
				
			||||||
pywin32
 | 
					pywin32
 | 
				
			||||||
python3-tk
 | 
					python3-tk
 | 
				
			||||||
 | 
					pystray
 | 
				
			||||||
							
								
								
									
										100
									
								
								resources.py
									
									
									
									
									
								
							
							
						
						
									
										100
									
								
								resources.py
									
									
									
									
									
								
							@ -1,100 +0,0 @@
 | 
				
			|||||||
 | 
					 | 
				
			||||||
class CLMgrMessage():
 | 
					 | 
				
			||||||
    CLMgrLineStateChangedMessage = 0      # state of at least one line has changed
 | 
					 | 
				
			||||||
    CLMgrLineSelectionChangedMessage = 1  # line in focus has changed
 | 
					 | 
				
			||||||
    CLMgrLineDetailsChangedMessage = 2    # details of at least one line have changed
 | 
					 | 
				
			||||||
    CLMgrCallDetailsMessage = 4           # details of last call are available, post mortem for logging purpose
 | 
					 | 
				
			||||||
    CLMgrServerDownMessage = 5            # server goes down, keep line manager, wait for ServerUp message
 | 
					 | 
				
			||||||
    CLMgrServerUpMessage = 6              # server is up again, keep interfaces to line manger
 | 
					 | 
				
			||||||
    CLMgrWaveDeviceChanged = 7            # speaker / micro has been switched on / off
 | 
					 | 
				
			||||||
    CLMgrGroupCallNotificationMessage = 8 # notification about group call
 | 
					 | 
				
			||||||
    CLMgrNumberOfLinesChangedMessage = 10 # the number of lines has changed
 | 
					 | 
				
			||||||
    CLMgrClientShutDownRequest = 11       # Client Line Manager requests client to shutdown and release all interfaces
 | 
					 | 
				
			||||||
    CLMgrLineStateChangedMessageEx = 28   # state of certain line has changed, lParam: LOWORD: line index of line that changed its state (starting with 0) HIWORD: new state of this line
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    s = [
 | 
					 | 
				
			||||||
        "CLMgrLineStateChangedMessage",
 | 
					 | 
				
			||||||
        "CLMgrLineSelectionChangedMessage",
 | 
					 | 
				
			||||||
        "CLMgrLineDetailsChangedMessage",
 | 
					 | 
				
			||||||
        "3",
 | 
					 | 
				
			||||||
        "CLMgrCallDetailsMessage",
 | 
					 | 
				
			||||||
        "CLMgrServerDownMessage",
 | 
					 | 
				
			||||||
        "CLMgrServerUpMessage",
 | 
					 | 
				
			||||||
        "CLMgrWaveDeviceChanged",
 | 
					 | 
				
			||||||
        "CLMgrGroupCallNotificationMessage",
 | 
					 | 
				
			||||||
        "9",
 | 
					 | 
				
			||||||
        "CLMgrNumberOfLinesChangedMessage",
 | 
					 | 
				
			||||||
        "CLMgrClientShutDownRequest",
 | 
					 | 
				
			||||||
        "12",
 | 
					 | 
				
			||||||
        "13",
 | 
					 | 
				
			||||||
        "14",
 | 
					 | 
				
			||||||
        "15",
 | 
					 | 
				
			||||||
        "16",
 | 
					 | 
				
			||||||
        "17",
 | 
					 | 
				
			||||||
        "18",
 | 
					 | 
				
			||||||
        "19",
 | 
					 | 
				
			||||||
        "20",
 | 
					 | 
				
			||||||
        "21",
 | 
					 | 
				
			||||||
        "22",
 | 
					 | 
				
			||||||
        "23",
 | 
					 | 
				
			||||||
        "24",
 | 
					 | 
				
			||||||
        "25",
 | 
					 | 
				
			||||||
        "26",
 | 
					 | 
				
			||||||
        "27",
 | 
					 | 
				
			||||||
        "CLMgrLineStateChangedMessageEx"
 | 
					 | 
				
			||||||
    ]
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class LineState():
 | 
					 | 
				
			||||||
    Inactive = 0                        # line is inactive
 | 
					 | 
				
			||||||
    HookOffInternal = 1                 # off hook, internal dialtone
 | 
					 | 
				
			||||||
    HookOffExternal = 2                 # off hook, external dialtone
 | 
					 | 
				
			||||||
    Ringing = 3                         # incoming call, ringing
 | 
					 | 
				
			||||||
    Dialing = 4                         # outgoing call, we are dialing, no sound
 | 
					 | 
				
			||||||
    Alerting = 5                        # outgoing call, alerting = ringing on destination
 | 
					 | 
				
			||||||
    Knocking = 6                        # outgoing call, knocking = second call ringing on destination
 | 
					 | 
				
			||||||
    Busy = 7                            # outgoing call, destination is busy
 | 
					 | 
				
			||||||
    Active = 8                          # incoming / outgoing call, logical and physical connection is established
 | 
					 | 
				
			||||||
    OnHold = 9                          # incoming / outgoing call, logical connection is established, destination gets music on hold
 | 
					 | 
				
			||||||
    ConferenceActive = 10               # incoming / outgoing conference, logical and physical connection is established
 | 
					 | 
				
			||||||
    ConferenceOnHold = 11               # incoming / outgoing conference, logical connection is established, not physcically connected
 | 
					 | 
				
			||||||
    Terminated = 12                     # incoming / outgoing connection / call has been disconnected
 | 
					 | 
				
			||||||
    Transferring = 13                   # special LSOnHold, call is awaiting to be transferred, peer gets special music on hold
 | 
					 | 
				
			||||||
    Disabled = 14                       # special LSInactive: wrap up time
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    s = [
 | 
					 | 
				
			||||||
        "Inactive",
 | 
					 | 
				
			||||||
        "HookOffInternal",
 | 
					 | 
				
			||||||
        "HookOffExternal",
 | 
					 | 
				
			||||||
        "Ringing",
 | 
					 | 
				
			||||||
        "Dialing",
 | 
					 | 
				
			||||||
        "Alerting",
 | 
					 | 
				
			||||||
        "Knocking",
 | 
					 | 
				
			||||||
        "Busy",
 | 
					 | 
				
			||||||
        "Active",
 | 
					 | 
				
			||||||
        "OnHold",
 | 
					 | 
				
			||||||
        "ConferenceActive",
 | 
					 | 
				
			||||||
        "ConferenceOnHold",
 | 
					 | 
				
			||||||
        "Terminated",
 | 
					 | 
				
			||||||
        "Transferring",
 | 
					 | 
				
			||||||
        "Disabled"
 | 
					 | 
				
			||||||
    ]
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class DisconnectReason():
 | 
					 | 
				
			||||||
    Normal = 0
 | 
					 | 
				
			||||||
    Busy = 1
 | 
					 | 
				
			||||||
    Rejected = 2
 | 
					 | 
				
			||||||
    Cancelled = 3
 | 
					 | 
				
			||||||
    Transferred = 4
 | 
					 | 
				
			||||||
    JoinedConference = 5
 | 
					 | 
				
			||||||
    NoAnswer = 6
 | 
					 | 
				
			||||||
    TooLate = 7
 | 
					 | 
				
			||||||
    DirectCallImpossible = 8
 | 
					 | 
				
			||||||
    WrongNumber = 9
 | 
					 | 
				
			||||||
    Unreachable = 10
 | 
					 | 
				
			||||||
    CallDiverted = 11
 | 
					 | 
				
			||||||
    CallRoutingFailed = 12
 | 
					 | 
				
			||||||
    PermissionDenied = 13
 | 
					 | 
				
			||||||
    NetworkCongestion = 14
 | 
					 | 
				
			||||||
    NoChannelAvailable = 15
 | 
					 | 
				
			||||||
    NumberChanged = 16
 | 
					 | 
				
			||||||
    IncompatibleDestination = 17
 | 
					 | 
				
			||||||
							
								
								
									
										44
									
								
								swyx-media-control.spec
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										44
									
								
								swyx-media-control.spec
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,44 @@
 | 
				
			|||||||
 | 
					# -*- mode: python ; coding: utf-8 -*-
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					block_cipher = None
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					a = Analysis(['gui.py'],
 | 
				
			||||||
 | 
					             pathex=['X:\\Documents\\Projekte\\Simon\\python media controll'],
 | 
				
			||||||
 | 
					             binaries=[],
 | 
				
			||||||
 | 
					             datas=[('favicon.ico', '.')],
 | 
				
			||||||
 | 
					             hiddenimports=[],
 | 
				
			||||||
 | 
					             hookspath=[],
 | 
				
			||||||
 | 
					             hooksconfig={},
 | 
				
			||||||
 | 
					             runtime_hooks=[],
 | 
				
			||||||
 | 
					             excludes=[],
 | 
				
			||||||
 | 
					             win_no_prefer_redirects=False,
 | 
				
			||||||
 | 
					             win_private_assemblies=False,
 | 
				
			||||||
 | 
					             cipher=block_cipher,
 | 
				
			||||||
 | 
					             noarchive=False)
 | 
				
			||||||
 | 
					pyz = PYZ(a.pure, a.zipped_data,
 | 
				
			||||||
 | 
					             cipher=block_cipher)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					exe = EXE(pyz,
 | 
				
			||||||
 | 
					          a.scripts, 
 | 
				
			||||||
 | 
					          [],
 | 
				
			||||||
 | 
					          exclude_binaries=True,
 | 
				
			||||||
 | 
					          name='swyx-media-control',
 | 
				
			||||||
 | 
					          debug=False,
 | 
				
			||||||
 | 
					          bootloader_ignore_signals=False,
 | 
				
			||||||
 | 
					          strip=False,
 | 
				
			||||||
 | 
					          upx=True,
 | 
				
			||||||
 | 
					          console=False,
 | 
				
			||||||
 | 
					          disable_windowed_traceback=False,
 | 
				
			||||||
 | 
					          target_arch=None,
 | 
				
			||||||
 | 
					          codesign_identity=None,
 | 
				
			||||||
 | 
					          entitlements_file=None , icon='favicon.ico')
 | 
				
			||||||
 | 
					coll = COLLECT(exe,
 | 
				
			||||||
 | 
					               a.binaries,
 | 
				
			||||||
 | 
					               a.zipfiles,
 | 
				
			||||||
 | 
					               a.datas, 
 | 
				
			||||||
 | 
					               strip=False,
 | 
				
			||||||
 | 
					               upx=True,
 | 
				
			||||||
 | 
					               upx_exclude=[],
 | 
				
			||||||
 | 
					               name='swyx-media-control')
 | 
				
			||||||
							
								
								
									
										221
									
								
								swyxcontrol.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										221
									
								
								swyxcontrol.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,221 @@
 | 
				
			|||||||
 | 
					import asyncio
 | 
				
			||||||
 | 
					from threading import Thread
 | 
				
			||||||
 | 
					import logging
 | 
				
			||||||
 | 
					import time
 | 
				
			||||||
 | 
					import logging
 | 
				
			||||||
 | 
					import pythoncom
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class CLMgrMessage():
 | 
				
			||||||
 | 
					    CLMgrLineStateChangedMessage = 0      # state of at least one line has changed
 | 
				
			||||||
 | 
					    CLMgrLineSelectionChangedMessage = 1  # line in focus has changed
 | 
				
			||||||
 | 
					    CLMgrLineDetailsChangedMessage = 2    # details of at least one line have changed
 | 
				
			||||||
 | 
					    CLMgrCallDetailsMessage = 4           # details of last call are available, post mortem for logging purpose
 | 
				
			||||||
 | 
					    CLMgrServerDownMessage = 5            # server goes down, keep line manager, wait for ServerUp message
 | 
				
			||||||
 | 
					    CLMgrServerUpMessage = 6              # server is up again, keep interfaces to line manger
 | 
				
			||||||
 | 
					    CLMgrWaveDeviceChanged = 7            # speaker / micro has been switched on / off
 | 
				
			||||||
 | 
					    CLMgrGroupCallNotificationMessage = 8 # notification about group call
 | 
				
			||||||
 | 
					    CLMgrNumberOfLinesChangedMessage = 10 # the number of lines has changed
 | 
				
			||||||
 | 
					    CLMgrClientShutDownRequest = 11       # Client Line Manager requests client to shutdown and release all interfaces
 | 
				
			||||||
 | 
					    CLMgrLineStateChangedMessageEx = 28   # state of certain line has changed, lParam: LOWORD: line index of line that changed its state (starting with 0) HIWORD: new state of this line
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    s = [
 | 
				
			||||||
 | 
					        "CLMgrLineStateChangedMessage",
 | 
				
			||||||
 | 
					        "CLMgrLineSelectionChangedMessage",
 | 
				
			||||||
 | 
					        "CLMgrLineDetailsChangedMessage",
 | 
				
			||||||
 | 
					        "3",
 | 
				
			||||||
 | 
					        "CLMgrCallDetailsMessage",
 | 
				
			||||||
 | 
					        "CLMgrServerDownMessage",
 | 
				
			||||||
 | 
					        "CLMgrServerUpMessage",
 | 
				
			||||||
 | 
					        "CLMgrWaveDeviceChanged",
 | 
				
			||||||
 | 
					        "CLMgrGroupCallNotificationMessage",
 | 
				
			||||||
 | 
					        "9",
 | 
				
			||||||
 | 
					        "CLMgrNumberOfLinesChangedMessage",
 | 
				
			||||||
 | 
					        "CLMgrClientShutDownRequest",
 | 
				
			||||||
 | 
					        "12",
 | 
				
			||||||
 | 
					        "13",
 | 
				
			||||||
 | 
					        "14",
 | 
				
			||||||
 | 
					        "15",
 | 
				
			||||||
 | 
					        "16",
 | 
				
			||||||
 | 
					        "17",
 | 
				
			||||||
 | 
					        "18",
 | 
				
			||||||
 | 
					        "19",
 | 
				
			||||||
 | 
					        "20",
 | 
				
			||||||
 | 
					        "21",
 | 
				
			||||||
 | 
					        "22",
 | 
				
			||||||
 | 
					        "23",
 | 
				
			||||||
 | 
					        "24",
 | 
				
			||||||
 | 
					        "25",
 | 
				
			||||||
 | 
					        "26",
 | 
				
			||||||
 | 
					        "27",
 | 
				
			||||||
 | 
					        "CLMgrLineStateChangedMessageEx"
 | 
				
			||||||
 | 
					    ]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class LineState():
 | 
				
			||||||
 | 
					    Inactive = 0                        # line is inactive
 | 
				
			||||||
 | 
					    HookOffInternal = 1                 # off hook, internal dialtone
 | 
				
			||||||
 | 
					    HookOffExternal = 2                 # off hook, external dialtone
 | 
				
			||||||
 | 
					    Ringing = 3                         # incoming call, ringing
 | 
				
			||||||
 | 
					    Dialing = 4                         # outgoing call, we are dialing, no sound
 | 
				
			||||||
 | 
					    Alerting = 5                        # outgoing call, alerting = ringing on destination
 | 
				
			||||||
 | 
					    Knocking = 6                        # outgoing call, knocking = second call ringing on destination
 | 
				
			||||||
 | 
					    Busy = 7                            # outgoing call, destination is busy
 | 
				
			||||||
 | 
					    Active = 8                          # incoming / outgoing call, logical and physical connection is established
 | 
				
			||||||
 | 
					    OnHold = 9                          # incoming / outgoing call, logical connection is established, destination gets music on hold
 | 
				
			||||||
 | 
					    ConferenceActive = 10               # incoming / outgoing conference, logical and physical connection is established
 | 
				
			||||||
 | 
					    ConferenceOnHold = 11               # incoming / outgoing conference, logical connection is established, not physcically connected
 | 
				
			||||||
 | 
					    Terminated = 12                     # incoming / outgoing connection / call has been disconnected
 | 
				
			||||||
 | 
					    Transferring = 13                   # special LSOnHold, call is awaiting to be transferred, peer gets special music on hold
 | 
				
			||||||
 | 
					    Disabled = 14                       # special LSInactive: wrap up time
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    s = [
 | 
				
			||||||
 | 
					        "Inactive",
 | 
				
			||||||
 | 
					        "HookOffInternal",
 | 
				
			||||||
 | 
					        "HookOffExternal",
 | 
				
			||||||
 | 
					        "Ringing",
 | 
				
			||||||
 | 
					        "Dialing",
 | 
				
			||||||
 | 
					        "Alerting",
 | 
				
			||||||
 | 
					        "Knocking",
 | 
				
			||||||
 | 
					        "Busy",
 | 
				
			||||||
 | 
					        "Active",
 | 
				
			||||||
 | 
					        "OnHold",
 | 
				
			||||||
 | 
					        "ConferenceActive",
 | 
				
			||||||
 | 
					        "ConferenceOnHold",
 | 
				
			||||||
 | 
					        "Terminated",
 | 
				
			||||||
 | 
					        "Transferring",
 | 
				
			||||||
 | 
					        "Disabled"
 | 
				
			||||||
 | 
					    ]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class DisconnectReason():
 | 
				
			||||||
 | 
					    Normal = 0
 | 
				
			||||||
 | 
					    Busy = 1
 | 
				
			||||||
 | 
					    Rejected = 2
 | 
				
			||||||
 | 
					    Cancelled = 3
 | 
				
			||||||
 | 
					    Transferred = 4
 | 
				
			||||||
 | 
					    JoinedConference = 5
 | 
				
			||||||
 | 
					    NoAnswer = 6
 | 
				
			||||||
 | 
					    TooLate = 7
 | 
				
			||||||
 | 
					    DirectCallImpossible = 8
 | 
				
			||||||
 | 
					    WrongNumber = 9
 | 
				
			||||||
 | 
					    Unreachable = 10
 | 
				
			||||||
 | 
					    CallDiverted = 11
 | 
				
			||||||
 | 
					    CallRoutingFailed = 12
 | 
				
			||||||
 | 
					    PermissionDenied = 13
 | 
				
			||||||
 | 
					    NetworkCongestion = 14
 | 
				
			||||||
 | 
					    NoChannelAvailable = 15
 | 
				
			||||||
 | 
					    NumberChanged = 16
 | 
				
			||||||
 | 
					    IncompatibleDestination = 17
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class SwyxControlException(Exception):
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    def __init__(self, msg, *args: object) -> None:
 | 
				
			||||||
 | 
					        self.msg = msg
 | 
				
			||||||
 | 
					        super().__init__(*args)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class SwyxControl(Thread):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    __running = True
 | 
				
			||||||
 | 
					    phone_mgr = None
 | 
				
			||||||
 | 
					    __line_state_changed_hander = []
 | 
				
			||||||
 | 
					    __phone_mgr_connected_hander = []
 | 
				
			||||||
 | 
					    all_lines_inactive = False
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    class __PhoneLineEventHandler():
 | 
				
			||||||
 | 
					        outer: 'SwyxControl' = None
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        def OnDispOnLineMgrNotification(self, msg, param, returns=""):
 | 
				
			||||||
 | 
					            if not self.outer:
 | 
				
			||||||
 | 
					                logging.debug("self.outer not set")
 | 
				
			||||||
 | 
					                return True
 | 
				
			||||||
 | 
					            logging.debug("msg: {}\tparam: {}\treturns: {}".format(msg, param,returns))
 | 
				
			||||||
 | 
					            if msg == CLMgrMessage.CLMgrLineStateChangedMessage:
 | 
				
			||||||
 | 
					                self.outer._handle_line_state_changed(msg, param)
 | 
				
			||||||
 | 
					            return True
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        def setOuter(self, outer: 'SwyxControl'):
 | 
				
			||||||
 | 
					            logging.debug("set outer class")
 | 
				
			||||||
 | 
					            self.outer = outer
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def _handle_line_state_changed(self, msg, param):
 | 
				
			||||||
 | 
					        line = self.phone_mgr.DispGetLine(param)
 | 
				
			||||||
 | 
					        logging.debug("Line: {} {} / {}".format(
 | 
				
			||||||
 | 
					            param,
 | 
				
			||||||
 | 
					            LineState.s[line.DispState],
 | 
				
			||||||
 | 
					            LineState.s[msg]
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        self.all_lines_inactive = self.__all_lines_inactive()
 | 
				
			||||||
 | 
					        for handler in self.__line_state_changed_hander:
 | 
				
			||||||
 | 
					            handler(line)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __init__(self) -> None:
 | 
				
			||||||
 | 
					        super().__init__()
 | 
				
			||||||
 | 
					        pass
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __all_lines_inactive(self)  -> bool:
 | 
				
			||||||
 | 
					        '''returns true if all lines are inactive'''
 | 
				
			||||||
 | 
					        for linenum in range(self.phone_mgr.DispNumberOfLines):
 | 
				
			||||||
 | 
					            line = self.phone_mgr.DispGetLine(linenum)
 | 
				
			||||||
 | 
					            if line.DispState != LineState.Inactive:
 | 
				
			||||||
 | 
					                return False
 | 
				
			||||||
 | 
					        return True
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def add_line_state_changed_handler(self, handler):
 | 
				
			||||||
 | 
					        '''handler called when CLMgrMessage.CLMgrLineStateChangedMessage
 | 
				
			||||||
 | 
					        is fired
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        def handler(line):
 | 
				
			||||||
 | 
					            if line.DispState == LineState.Ringing:
 | 
				
			||||||
 | 
					                print("Ringing")
 | 
				
			||||||
 | 
					            if line.DispState == LineState.HookOffInternal:
 | 
				
			||||||
 | 
					                print("HookOffInternal")
 | 
				
			||||||
 | 
					        sc.add_line_state_changed_handler(handler)
 | 
				
			||||||
 | 
					        '''
 | 
				
			||||||
 | 
					        self.__line_state_changed_hander.append(handler)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def add_phone_mgr_connected_handler(self, handler):
 | 
				
			||||||
 | 
					        '''
 | 
				
			||||||
 | 
					        handler called when CLMgr.ClientLineMgr dispatched
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        def handler():
 | 
				
			||||||
 | 
					            print("connected")
 | 
				
			||||||
 | 
					        sc.add_phone_mgr_connected_handler(handler)
 | 
				
			||||||
 | 
					        '''
 | 
				
			||||||
 | 
					        self.__phone_mgr_connected_hander.append(handler)
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    def stop(self):
 | 
				
			||||||
 | 
					        '''stops the running thread'''
 | 
				
			||||||
 | 
					        self.__running = False
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def run(self):
 | 
				
			||||||
 | 
					        # Initialize
 | 
				
			||||||
 | 
					        import win32com.client
 | 
				
			||||||
 | 
					        #pythoncom.CoInitialize(pythoncom.COINIT_MULTITHREADED)
 | 
				
			||||||
 | 
					        #try:
 | 
				
			||||||
 | 
					        self.phone_mgr = win32com.client.Dispatch("CLMgr.ClientLineMgr")
 | 
				
			||||||
 | 
					        self.all_lines_inactive = self.__all_lines_inactive()
 | 
				
			||||||
 | 
					        for handler in self.__phone_mgr_connected_hander:
 | 
				
			||||||
 | 
					            handler()
 | 
				
			||||||
 | 
					        # except Exception:
 | 
				
			||||||
 | 
					        #     raise SwyxControlException("Swyx Client not installed")
 | 
				
			||||||
 | 
					        self.e = win32com.client.WithEvents("CLMgr.ClientLineMgr", self.__PhoneLineEventHandler)
 | 
				
			||||||
 | 
					        self.e.setOuter(self)
 | 
				
			||||||
 | 
					        while self.__running:
 | 
				
			||||||
 | 
					            pythoncom.PumpWaitingMessages()
 | 
				
			||||||
 | 
					            time.sleep(0.1)
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					if __name__ == "__main__":
 | 
				
			||||||
 | 
					    logging.getLogger().setLevel(logging.DEBUG)
 | 
				
			||||||
 | 
					    def line_change_handler(line):
 | 
				
			||||||
 | 
					        if line.DispState == LineState.Ringing:
 | 
				
			||||||
 | 
					            print("Ringing")
 | 
				
			||||||
 | 
					        if line.DispState == LineState.HookOffInternal:
 | 
				
			||||||
 | 
					            print("HookOffInternal")
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    def phone_mgr_connected_handler():
 | 
				
			||||||
 | 
					        print(sc.all_lines_inactive)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    sc = SwyxControl()
 | 
				
			||||||
 | 
					    sc.add_line_state_changed_handler(line_change_handler)
 | 
				
			||||||
 | 
					    sc.add_phone_mgr_connected_handler(phone_mgr_connected_handler)
 | 
				
			||||||
 | 
					    sc.start()
 | 
				
			||||||
 | 
					    pass
 | 
				
			||||||
@ -1,9 +1,12 @@
 | 
				
			|||||||
from collections import defaultdict
 | 
					from collections import defaultdict
 | 
				
			||||||
from enum import Enum
 | 
					from enum import Enum
 | 
				
			||||||
 | 
					from threading import Thread
 | 
				
			||||||
import win32api
 | 
					import win32api
 | 
				
			||||||
import win32con
 | 
					import win32con
 | 
				
			||||||
import win32gui
 | 
					import win32gui
 | 
				
			||||||
import win32ts
 | 
					import win32ts
 | 
				
			||||||
 | 
					import time
 | 
				
			||||||
 | 
					import ctypes
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class SessionEvent(Enum):
 | 
					class SessionEvent(Enum):
 | 
				
			||||||
@ -22,14 +25,32 @@ class SessionEvent(Enum):
 | 
				
			|||||||
    SESSION_REMOTE_CONTROL = 0x9  # WTS_SESSION_REMOTE_CONTROL
 | 
					    SESSION_REMOTE_CONTROL = 0x9  # WTS_SESSION_REMOTE_CONTROL
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class WorkstationMonitor:
 | 
					class LockscreenMonitor(Thread):
 | 
				
			||||||
    CLASS_NAME = "WorkstationMonitor"
 | 
					    CLASS_NAME = "LockscreenMonitor"
 | 
				
			||||||
    WINDOW_TITLE = "Workstation Event Monitor"
 | 
					    WINDOW_TITLE = "Lockscreen Event Monitor"
 | 
				
			||||||
 | 
					    __running = True
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def __init__(self):
 | 
					    def __init__(self):
 | 
				
			||||||
        self.window_handle = None
 | 
					        self.window_handle = None
 | 
				
			||||||
        self.event_handlers = defaultdict(list)
 | 
					        self.event_handlers = defaultdict(list)
 | 
				
			||||||
        self._register_listener()
 | 
					        self._register_listener()
 | 
				
			||||||
 | 
					        super().__init__()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def run(self) -> None:
 | 
				
			||||||
 | 
					        while self.__running:
 | 
				
			||||||
 | 
					            self.listen()
 | 
				
			||||||
 | 
					            time.sleep(0.1)
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    def stop(self):
 | 
				
			||||||
 | 
					        self.__running = False
 | 
				
			||||||
 | 
					        exit_code = 0
 | 
				
			||||||
 | 
					        win32gui.PostQuitMessage(exit_code)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def screen_locked(self) -> bool:
 | 
				
			||||||
 | 
					        if (ctypes.windll.User32.GetForegroundWindow() % 10 == 0):
 | 
				
			||||||
 | 
					            return True 
 | 
				
			||||||
 | 
					        else:
 | 
				
			||||||
 | 
					            return False
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def _register_listener(self):
 | 
					    def _register_listener(self):
 | 
				
			||||||
        wc = win32gui.WNDCLASS()
 | 
					        wc = win32gui.WNDCLASS()
 | 
				
			||||||
@ -51,11 +72,7 @@ class WorkstationMonitor:
 | 
				
			|||||||
        win32ts.WTSRegisterSessionNotification(self.window_handle, scope)
 | 
					        win32ts.WTSRegisterSessionNotification(self.window_handle, scope)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def listen(self):
 | 
					    def listen(self):
 | 
				
			||||||
        win32gui.PumpMessages()
 | 
					        win32gui.PumpWaitingMessages()
 | 
				
			||||||
 | 
					 | 
				
			||||||
    def stop(self):
 | 
					 | 
				
			||||||
        exit_code = 0
 | 
					 | 
				
			||||||
        win32gui.PostQuitMessage(exit_code)
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def _window_procedure(self, window_handle: int, message: int, event_id, session_id):
 | 
					    def _window_procedure(self, window_handle: int, message: int, event_id, session_id):
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
@ -78,11 +95,11 @@ class WorkstationMonitor:
 | 
				
			|||||||
        for handler in self.event_handlers[SessionEvent.ANY]:
 | 
					        for handler in self.event_handlers[SessionEvent.ANY]:
 | 
				
			||||||
            handler(event)
 | 
					            handler(event)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def register_handler(self, event: SessionEvent, handler: callable):
 | 
					    def register_handler(self, handler: callable):
 | 
				
			||||||
        self.event_handlers[event].append(handler)
 | 
					        self.event_handlers[SessionEvent.ANY].append(handler)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
if __name__ == '__main__':
 | 
					if __name__ == '__main__':
 | 
				
			||||||
    m = WorkstationMonitor()
 | 
					    m = LockscreenMonitor()
 | 
				
			||||||
    m.register_handler(SessionEvent.ANY, handler=print)
 | 
					    m.register_handler(SessionEvent.ANY, handler=print)
 | 
				
			||||||
    m.listen()
 | 
					    m.listen()
 | 
				
			||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user