diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..26d3352 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,3 @@ +# Default ignored files +/shelf/ +/workspace.xml diff --git a/.idea/inspectionProfiles/profiles_settings.xml b/.idea/inspectionProfiles/profiles_settings.xml new file mode 100644 index 0000000..105ce2d --- /dev/null +++ b/.idea/inspectionProfiles/profiles_settings.xml @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..dc9ea49 --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..d85f0c0 --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/screen_status_to_mqtt.iml b/.idea/screen_status_to_mqtt.iml new file mode 100644 index 0000000..d0876a7 --- /dev/null +++ b/.idea/screen_status_to_mqtt.iml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..94a25f7 --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/main.py b/main.py new file mode 100644 index 0000000..6b17b51 --- /dev/null +++ b/main.py @@ -0,0 +1,76 @@ +import logging +import socket +import sys +from time import sleep + +import servicemanager +import win32event +import win32service +import win32serviceutil + +from win32_notification_receiver import Win32NotificationReceiver + +logPath = 'C:/Users/seb65/Documents/Gitea/screen_status_to_mqtt/' + +logging.basicConfig( + level=logging.DEBUG, + format="%(asctime)s [%(threadName)-12.12s] [%(levelname)-5.5s] %(message)s", + handlers=[ + logging.FileHandler(logPath + "debug.log"), + logging.StreamHandler() + ] +) + +logger = logging.getLogger() + + +class ScreenStatusToMqttService(win32serviceutil.ServiceFramework): + _svc_name_ = "ScreenStatusToMqtt" + _svc_display_name_ = "Screen Status To MQTT" + _svc_description_ = "Send your screen status to MQTT Broker" + + win32_notification_receiver: Win32NotificationReceiver = None + running_flag = False + + def __init__(self, args): + super().__init__(args) + self.hWaitStop = win32event.CreateEvent(None, 0, 0, None) + socket.setdefaulttimeout(60) + + def SvcStop(self): + logger.info("Stopping Service...") + self.running_flag = False + self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING) + win32event.SetEvent(self.hWaitStop) + + def SvcDoRun(self): + self.running_flag = True + servicemanager.LogMsg(servicemanager.EVENTLOG_INFORMATION_TYPE, + servicemanager.PYS_SERVICE_STARTED, + (self._svc_name_, '')) + if self.ssh is None: + logger.critical("Can't be run in debug mode !") + sys.exit(1) + else: + self.main() + + def SvcOtherEx(self, control, event_type, data): + if control == win32service.SERVICE_CONTROL_POWEREVENT: + self.win32_notification_receiver.event_handler(event_type, data) + else: + logger.warning("Unsupported control: ") + logger.warning(control) + + def main(self): + self.win32_notification_receiver = Win32NotificationReceiver(self.ssh) + while self.running_flag: + sleep(1) + + +if __name__ == '__main__': + if len(sys.argv) == 1: + servicemanager.Initialize() + servicemanager.PrepareToHostSingle(ScreenStatusToMqttService) + servicemanager.StartServiceCtrlDispatcher() + else: + win32serviceutil.HandleCommandLine(ScreenStatusToMqttService) diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..cfe3195 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,3 @@ +pywin32=303 +comtypes=1.1.11 +pyinstaller=4.10 \ No newline at end of file diff --git a/sm_win_service.py b/sm_win_service.py new file mode 100644 index 0000000..e69de29 diff --git a/win32_notification_receiver.py b/win32_notification_receiver.py new file mode 100644 index 0000000..64535af --- /dev/null +++ b/win32_notification_receiver.py @@ -0,0 +1,68 @@ +import threading + +import win32con +import win32api +import win32gui +import time +import logging +from ctypes import POINTER, windll, Structure, cast, CFUNCTYPE, c_int, c_uint, c_void_p, c_bool +from comtypes import GUID +from ctypes.wintypes import HANDLE, DWORD + +PBT_POWERSETTINGCHANGE = 0x8013 # 32787 +GUID_CONSOLE_DISPLAY_STATE = "{6FE69556-704A-47A0-8F24-C28D936FDA47}" +GUID_MONITOR_POWER_ON = "{02731015-4510-4526-99E6-E5A17EBD1AEA}" + +logger = logging.getLogger() + + +class Win32NotificationReceiver(): + running_flag = False + + def __init__(self, recipient): + super().__init__() + self.recipient = recipient + self.__prepare_win32gui_message() + + def __prepare_win32gui_message(self): + guids_info = { + "GUID_MONITOR_POWER_ON": GUID_MONITOR_POWER_ON, + "GUID_CONSOLE_DISPLAY_STATE": GUID_CONSOLE_DISPLAY_STATE, + } + logger.info("----") + for name, guid_info in guids_info.items(): + logger.info(self.recipient) + result = windll.user32.RegisterPowerSettingNotification(HANDLE(self.recipient), GUID(guid_info), DWORD(1)) + logger.info(f'registering {name}') + logger.info(f"result: {hex(result)}") + logger.info(f"lastError: {win32api.GetLastError()}") + logger.info("----") + + def event_handler(self, event_type, raw_data): + if event_type == PBT_POWERSETTINGCHANGE: + logger.debug("Power setting changed...") + logger.debug("raw_data:") + logger.debug(raw_data) + + power_setting = str(raw_data[0]) + logger.debug("Power Setting: ") + logger.debug(power_setting) + + data = list(raw_data[1]) + logger.debug("Data: ") + logger.debug(data) + + if power_setting == GUID_CONSOLE_DISPLAY_STATE: + if data[0] == 0: + logger.debug("Display off") + if data[0] == 1: + logger.debug("Display on") + if data[0] == 2: + logger.debug("Display dimmed") + elif power_setting == GUID_MONITOR_POWER_ON: + if data[0] == 0: + logger.debug("Monitor off") + if data[0] == 1: + logger.debug("Monitor on") + else: + logger.warning("unknown GUID")