From 34166d0ccf32ce8ca957d68e7df4e9f7860bfcc4 Mon Sep 17 00:00:00 2001 From: "Antonio J. Delgado" Date: Mon, 6 Jan 2025 22:20:20 +0200 Subject: [PATCH] Add mvp --- .../publish_active_windows.py | 122 +++++++++++++++++- requirements.txt | 3 +- 2 files changed, 122 insertions(+), 3 deletions(-) diff --git a/publish_active_windows/publish_active_windows.py b/publish_active_windows/publish_active_windows.py index da295ac..554c602 100644 --- a/publish_active_windows/publish_active_windows.py +++ b/publish_active_windows/publish_active_windows.py @@ -9,8 +9,15 @@ import sys import os import logging from logging.handlers import SysLogHandler +import subprocess +import re +import json +import time +from datetime import datetime, timezone +import socket import click import click_config_file +import requests class PublishActiveWindows: @@ -30,6 +37,101 @@ class PublishActiveWindows: 'publish_active_windows.log' ) self._init_log() + self.session = requests.Session() + hostname = socket.gethostname() + while True: + windows = self._get_windows_list() + self._log.debug( + json.dumps(windows, indent=2) + ) + self._log.debug( + 'Sleeping %s seconds', + self.config['update_frequency'] + ) + for window in windows: + command_line = self._get_process_command_line(window['pid']) + if window['title']: + state = window['title'] + else: + state = command_line + share_attributes = { + "command_line": command_line, + "hostname": hostname + } + window['pid'] = f"{window['pid']}" + window['id'] = f"{window['id']}" + window['maximized'] = f"{window['maximized']}" + attributes = {**share_attributes, **window} + if window['focus']: + entity_id = f'sensor.{hostname}_active_window' + attributes['focus'] = 'True' + else: + entity_id = f'sensor.{hostname}_open_window' + attributes['focus'] = 'False' + if not self._publish_state(entity_id=entity_id, state=state, attributes=attributes): + sys.exit(1) + time.sleep(self.config['update_frequency']) + + def _get_process_command_line(self, process_id): + with open(f"/proc/{process_id}/cmdline", 'r', encoding='utf-8') as cmdline_file: + raw_cmdline = cmdline_file.read() + return ' '.join(raw_cmdline.split('\0')).strip() + return None + + def _publish_state(self, entity_id, state, attributes): + now = datetime.now(timezone.utc) + data = { + "state": state, + "attributes": attributes, + "last_changed": now.strftime("%Y-%m-%dT%H:%M:%S.%f+00:00"), + "last_updated": now.strftime("%Y-%m-%dT%H:%M:%S.%f+00:00") + } + headers = { + "Authorization": f"Bearer {self.config['ha_token']}", + "Content-Type": "application/json" + } + uri = f"{self.config['ha_url']}/api/states/{entity_id}" + self._log.debug( + "Publishing data to HomeAssistant in '%s'. Headers: %s. Data: '%s", + uri, + headers, + json.dumps(data, indent=2) + ) + result = self.session.post( + uri, + json=data, + headers=headers + ) + if result.status_code > 399: + self._log.error( + "Result: %s. %s. %s", + result.status_code, + result.reason, + result.text + ) + return None + self._log.debug( + "Result: %s. %s. %s", + result.status_code, + result.reason, + result.text + ) + return result.json + + def _get_windows_list(self): + command = [ + 'gdbus', + 'call', + '--session', + '--dest', + 'org.gnome.Shell', + '--object-path', + '/org/gnome/Shell/Extensions/WindowsExt', + '--method', + 'org.gnome.Shell.Extensions.WindowsExt.List' + ] + result = subprocess.run(command, capture_output=True, check=True) + return json.loads(re.sub(b"',\\)$", b'', re.sub(b"^\\('", b'', result.stdout))) def _init_log(self): ''' Initialize log object ''' @@ -83,8 +185,24 @@ class PublishActiveWindows: help='Set the debug level for the standard output.' ) @click.option('--log-file', '-l', help="File to store all debug messages.") -# @click.option("--dummy","-n", is_flag=True, -# help="Don't do anything, just show what would be done.") +@click.option( + '--ha-token', + '-t', + required=True, + help='Token to talk with HomeAssistant' +) +@click.option( + '--ha-url', + '-u', + required=True, + help='HomeAssistant URL' +) +@click.option( + '--update-frequency', + '-f', + default=60, + help='Frequency in seconds to update HomeAssistant status' +) @click_config_file.configuration_option() def __main__(**kwargs): return PublishActiveWindows(**kwargs) diff --git a/requirements.txt b/requirements.txt index 66bf966..46c4eb9 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,2 +1,3 @@ click -click_config_file \ No newline at end of file +click_config_file +requests