From 38a07affbe33f14296a50ba034b792a1c2d24566 Mon Sep 17 00:00:00 2001 From: bkbilly Date: Tue, 31 Jan 2023 18:33:38 +0200 Subject: [PATCH] camera status, support number control, add enabled option, replace pynput with xdotool key, discovery fix --- README.md | 14 +++---- install.sh | 2 +- lnxlink/__main__.py | 39 ++++++++++--------- lnxlink/modules/camera_used.py | 19 +++++++++ lnxlink/modules/cpu.py | 1 + lnxlink/modules/media.py | 7 +++- lnxlink/modules/memory.py | 1 + .../{microphone.py => microphone_used.py} | 0 lnxlink/modules/send_keys.py | 35 ++++++----------- pyproject.toml | 3 +- 10 files changed, 68 insertions(+), 53 deletions(-) create mode 100644 lnxlink/modules/camera_used.py rename lnxlink/modules/{microphone.py => microphone_used.py} (100%) diff --git a/README.md b/README.md index 3c89f38..a75edfc 100644 --- a/README.md +++ b/README.md @@ -29,7 +29,7 @@ It's very usefull for remote controling a linux PC, receiving notifications and # Installation Install or update: ```shell -sudo apt install patchelf meson libdbus-glib-1-dev libglib2.0-dev libasound2-dev python3-pip +sudo apt install patchelf meson libdbus-glib-1-dev libglib2.0-dev libasound2-dev python3-pip xdotool xprintidle xdg-utils pip3 install -U lnxlink # When asked, it's recommended to install as a user service. lnxlink -c config.yaml @@ -66,20 +66,20 @@ data: "iconUrl": "http://hass.local:8123/local/myimage.jpg" } ``` -### Send a series of keys: +### Send a command: ```yaml service: mqtt.publish data: - topic: {prefix}/{clientId}/commands/send_keys - payload: "+t" + topic: {prefix}/{clientId}/commands/bash + payload: "ctrl+shift+t" ``` -### Send a command: +### Send a series of keys: ```yaml service: mqtt.publish data: - topic: {prefix}/{clientId}/commands/bash - payload: "xdotool key ctrl+t" + topic: {prefix}/{clientId}/commands/send_keys + payload: "ctrl+f H e l l o space W o r l d" ``` ### Open a URL or a File diff --git a/install.sh b/install.sh index c6953dc..72c7f1a 100755 --- a/install.sh +++ b/install.sh @@ -43,7 +43,7 @@ fi if [ system != 'debian/ubuntu' ]; then echo -e "\n\n\e[31mSystem dependencies might not be correct...\e[0m" fi -sudo $installcommand patchelf meson libdbus-glib-1-dev libglib2.0-dev libasound2-dev +sudo $installcommand patchelf meson libdbus-glib-1-dev libglib2.0-dev libasound2-dev xdotool xprintidle xdg-utils # Install Python requirements diff --git a/lnxlink/__main__.py b/lnxlink/__main__.py index badccfe..46be1e3 100755 --- a/lnxlink/__main__.py +++ b/lnxlink/__main__.py @@ -149,14 +149,14 @@ def on_message(self, client, userdata, msg): def setup_discovery_monitoring(self, addon, service, discovery_template): subtopic = addon.name.lower().replace(' ', '/') - topic = f"{self.pref_topic}/{self.config['mqtt']['statsPrefix']}/{subtopic}" + state_topic = f"{self.pref_topic}/{self.config['mqtt']['statsPrefix']}/{subtopic}" discovery = discovery_template.copy() discovery['name'] = addon.name.lower().replace(' ', '_') discovery['unique_id'] = f"{self.config['mqtt']['clientId']}_{service}" - discovery['state_topic'] = topic + discovery['state_topic'] = state_topic if addon.getInfo.__annotations__.get('return') == dict: - discovery['json_attributes_topic'] = topic + discovery['json_attributes_topic'] = state_topic if hasattr(addon, 'icon'): discovery['icon'] = addon.icon if hasattr(addon, 'unit'): @@ -174,35 +174,38 @@ def setup_discovery_monitoring(self, addon, service, discovery_template): if hasattr(addon, 'state_class'): discovery['state_class'] = addon.state_class - - if hasattr(addon, 'sensor_type'): - sensor_type = getattr(addon, 'sensor_type', 'sensor') - if sensor_type in ['binary_sensor', 'sensor', 'update']: - self.client.publish( - f"homeassistant/{sensor_type}/lnxlink/{discovery['unique_id']}/config", - payload=json.dumps(discovery), - retain=self.config['mqtt']['lwt']['retain']) + sensor_type = getattr(addon, 'sensor_type', 'sensor') + self.client.publish( + f"homeassistant/{sensor_type}/lnxlink/{discovery['unique_id']}/config", + payload=json.dumps(discovery), + retain=self.config['mqtt']['lwt']['retain']) def setup_discovery_control(self, addon, service, control_name, options, discovery_template): subtopic = addon.name.lower().replace(' ', '/') + state_topic = f"{self.pref_topic}/{self.config['mqtt']['statsPrefix']}/{subtopic}" discovery = discovery_template.copy() discovery['name'] = control_name.lower().replace(' ', '_') discovery['unique_id'] = f"{self.config['mqtt']['clientId']}_{control_name}" - discovery['icon'] = options.get('icon', '') + discovery['enabled_by_default'] = options.get('enabled', True) + discovery["command_topic"] = f"{self.pref_topic}/commands/{service}/{control_name}/" + if 'value_template' in options: + discovery["value_template"] = options['value_template'] + if 'icon' in options: + discovery['icon'] = options.get('icon', '') + if options['type'] == 'button': discovery['state_topic'] = f"{self.pref_topic}/lwt" - discovery["command_topic"] = f"{self.pref_topic}/commands/{service}/{control_name}/" elif options['type'] == 'switch': - discovery["state_topic"] = f"{self.pref_topic}/{self.config['mqtt']['statsPrefix']}/{subtopic}" - discovery["command_topic"] = f"{self.pref_topic}/commands/{service}/{control_name}/" + discovery["state_topic"] = state_topic discovery["payload_off"] = "OFF" discovery["payload_on"] = "ON" elif options['type'] == 'text': - discovery["state_topic"] = f"{self.pref_topic}/{self.config['mqtt']['statsPrefix']}/{subtopic}" - discovery["command_topic"] = f"{self.pref_topic}/commands/{service}/{control_name}/" + discovery["state_topic"] = state_topic elif options['type'] == 'number': - return + discovery["state_topic"] = state_topic + discovery["min"] = options.get('min', 1) + discovery["max"] = options.get('max', 100) else: return self.client.publish( diff --git a/lnxlink/modules/camera_used.py b/lnxlink/modules/camera_used.py new file mode 100644 index 0000000..c9d9b45 --- /dev/null +++ b/lnxlink/modules/camera_used.py @@ -0,0 +1,19 @@ +import subprocess + + +class Addon(): + name = 'Camera used' + icon = 'mdi:webcam' + sensor_type = 'binary_sensor' + + def getInfo(self): + stdout = subprocess.run( + "lsmod | grep '^uvcvideo ' | awk '{print $3}'", + shell=True, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE).stdout.decode("UTF-8") + + cam_used = bool(int(stdout.strip())) + if cam_used: + return "ON" + return "OFF" diff --git a/lnxlink/modules/cpu.py b/lnxlink/modules/cpu.py index e906a22..a7113ef 100644 --- a/lnxlink/modules/cpu.py +++ b/lnxlink/modules/cpu.py @@ -4,6 +4,7 @@ class Addon(): name = 'CPU Usage' icon = 'mdi:speedometer' unit = '%' + state_class = 'measurement' def getInfo(self): return psutil.cpu_percent() diff --git a/lnxlink/modules/media.py b/lnxlink/modules/media.py index 08354a7..a57217a 100644 --- a/lnxlink/modules/media.py +++ b/lnxlink/modules/media.py @@ -18,20 +18,25 @@ def exposedControls(self): "playpause": { "type": "button", "icon": "mdi:play-pause", + "enabled": False, }, "previous": { "type": "button", "icon": "mdi:skip-previous", + "enabled": False, }, "next": { "type": "button", "icon": "mdi:skip-next", + "enabled": False, }, "volume_set": { "type": "number", - "icon": "mdi:volume", + "icon": "mdi:volume-high", "min": 0, "max": 100, + "enabled": False, + "value_template": "{{ value_json.volume }}", } } diff --git a/lnxlink/modules/memory.py b/lnxlink/modules/memory.py index 124c22d..1e61590 100644 --- a/lnxlink/modules/memory.py +++ b/lnxlink/modules/memory.py @@ -4,6 +4,7 @@ class Addon(): name = 'Memory Usage' icon = 'mdi:memory' unit = '%' + state_class = 'measurement' def getInfo(self): return psutil.virtual_memory().percent diff --git a/lnxlink/modules/microphone.py b/lnxlink/modules/microphone_used.py similarity index 100% rename from lnxlink/modules/microphone.py rename to lnxlink/modules/microphone_used.py diff --git a/lnxlink/modules/send_keys.py b/lnxlink/modules/send_keys.py index 149c388..fcc0c9b 100644 --- a/lnxlink/modules/send_keys.py +++ b/lnxlink/modules/send_keys.py @@ -1,29 +1,16 @@ -# from pynput.keyboard import Key, Controller -from pynput import keyboard +import subprocess + class Addon(): name = 'Send Keys' - icon = None - unit = None - - def startControl(self, topic, data): - if type(data) == str: - self.__presskeys(data) - else: - for keys in data: - self.__presskeys(keys) - def __presskeys(self, keys): - contr = keyboard.Controller() - hotkeys = keyboard.HotKey.parse(keys) - for hotkey in hotkeys: - contr.press(hotkey) - hotkeys.reverse() - for hotkey in hotkeys: - contr.release(hotkey) + def exposedControls(self): + return { + "Send_Keys": { + "type": "text", + "icon": "mdi:keyboard-outline", + } + } - -if __name__ == '__main__': - myaddon = Addon() - # myaddon.startControl('send-keys', ['+', '++', '+C']) - # myaddon.startControl('send-keys', ['+C', 'c']) + def startControl(self, topic, data): + subprocess.call(f"xdotool key {data}", shell=True) diff --git a/pyproject.toml b/pyproject.toml index 9a68cc5..a40a91f 100755 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "lnxlink" -version = "2023.1.5" +version = "2023.2.0" description = "Internet Of Things (IOT) integration with Linux using MQTT" readme = "README.md" keywords = ["lnxlink"] @@ -23,7 +23,6 @@ dependencies = [ "notify2>=0.3.1", "psutil>=5.8.0", "mpris2>=1.0.2", - "pynput>=1.7.3", "pyalsaaudio>=0.9.2", "dbus-python>=1.3.2", "pgi>=0.0.11.2",