refactoring as gui application

:# Please enter the commit message for your changes. Lines starting
This commit is contained in:
Simon Zeyer 2021-12-29 15:14:33 +01:00
parent 0b8f3ad9b6
commit 65f3a5c1da
11 changed files with 1177 additions and 275 deletions

View File

@ -1,155 +1,5 @@
import asyncio
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 gui import Gui
from resources import LineState, CLMgrMessage
import serial
from serial import SerialException
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")
if __name__ == "__main__":
gui = Gui()

View File

@ -22,23 +22,19 @@ pyz = PYZ(a.pure, a.zipped_data,
exe = EXE(pyz,
a.scripts,
a.binaries,
a.zipfiles,
a.datas,
[],
exclude_binaries=True,
name='__main__',
debug=False,
bootloader_ignore_signals=False,
strip=False,
upx=True,
upx_exclude=[],
runtime_tmpdir=None,
console=True,
disable_windowed_traceback=False,
target_arch=None,
codesign_identity=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

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

147
gui.py Normal file
View 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
View 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
View 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)

View File

@ -1,3 +1,4 @@
winrt
pywin32
python3-tk
pystray

View File

@ -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
View 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
View 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

View File

@ -1,9 +1,12 @@
from collections import defaultdict
from enum import Enum
from threading import Thread
import win32api
import win32con
import win32gui
import win32ts
import time
import ctypes
class SessionEvent(Enum):
@ -22,14 +25,32 @@ class SessionEvent(Enum):
SESSION_REMOTE_CONTROL = 0x9 # WTS_SESSION_REMOTE_CONTROL
class WorkstationMonitor:
CLASS_NAME = "WorkstationMonitor"
WINDOW_TITLE = "Workstation Event Monitor"
class LockscreenMonitor(Thread):
CLASS_NAME = "LockscreenMonitor"
WINDOW_TITLE = "Lockscreen Event Monitor"
__running = True
def __init__(self):
self.window_handle = None
self.event_handlers = defaultdict(list)
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):
wc = win32gui.WNDCLASS()
@ -51,11 +72,7 @@ class WorkstationMonitor:
win32ts.WTSRegisterSessionNotification(self.window_handle, scope)
def listen(self):
win32gui.PumpMessages()
def stop(self):
exit_code = 0
win32gui.PostQuitMessage(exit_code)
win32gui.PumpWaitingMessages()
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]:
handler(event)
def register_handler(self, event: SessionEvent, handler: callable):
self.event_handlers[event].append(handler)
def register_handler(self, handler: callable):
self.event_handlers[SessionEvent.ANY].append(handler)
if __name__ == '__main__':
m = WorkstationMonitor()
m = LockscreenMonitor()
m.register_handler(SessionEvent.ANY, handler=print)
m.listen()