Skip to content

Commit 75d74e8

Browse files
committed
Add Clipboard module
1 parent bf47668 commit 75d74e8

1 file changed

Lines changed: 164 additions & 0 deletions

File tree

lnxlink/modules/clipboard.py

Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
1+
"""Set and monitor clipboard content"""
2+
import os
3+
import logging
4+
from shutil import which
5+
from lnxlink.modules.scripts.helpers import syscommand
6+
7+
logger = logging.getLogger("lnxlink")
8+
9+
10+
class Addon:
11+
"""Addon module"""
12+
13+
def __init__(self, lnxlink):
14+
"""Setup addon"""
15+
self.name = "Clipboard"
16+
self.lnxlink = lnxlink
17+
self.session_type = os.environ.get("XDG_SESSION_TYPE", "unknown").lower()
18+
self.clipboard_tool = None
19+
self.last_clipboard = ""
20+
21+
# Add settings with monitoring disabled by default
22+
self.lnxlink.add_settings(
23+
"clipboard",
24+
{
25+
"monitor_enabled": False,
26+
},
27+
)
28+
29+
# Determine which clipboard tool to use
30+
if self.session_type == "wayland":
31+
if which("wl-copy") is not None and which("wl-paste") is not None:
32+
self.clipboard_tool = "wl-clipboard"
33+
logger.debug("Using wl-clipboard for Wayland")
34+
else:
35+
raise SystemError(
36+
"System command 'wl-clipboard' (wl-copy/wl-paste) not found"
37+
)
38+
elif self.session_type == "x11":
39+
if which("xclip") is not None:
40+
self.clipboard_tool = "xclip"
41+
logger.debug("Using xclip for X11")
42+
elif which("xsel") is not None:
43+
self.clipboard_tool = "xsel"
44+
logger.debug("Using xsel for X11")
45+
else:
46+
raise SystemError("System command 'xclip' or 'xsel' not found")
47+
else:
48+
raise SystemError(f"Unsupported session type: {self.session_type}")
49+
50+
def exposed_controls(self):
51+
"""Exposes to home assistant"""
52+
discovery_info = {
53+
"Clipboard Set": {
54+
"type": "text",
55+
"icon": "mdi:clipboard-text",
56+
"max": 255,
57+
}
58+
}
59+
60+
# Only add monitor sensor if enabled in settings
61+
if self.lnxlink.config["settings"]["clipboard"]["monitor_enabled"]:
62+
discovery_info["Clipboard Monitor"] = {
63+
"type": "sensor",
64+
"icon": "mdi:clipboard-check",
65+
"entity_category": "diagnostic",
66+
}
67+
68+
return discovery_info
69+
70+
def get_info(self):
71+
"""Gather information from the system"""
72+
# Only monitor clipboard if enabled in settings
73+
if not self.lnxlink.config["settings"]["clipboard"]["monitor_enabled"]:
74+
return None
75+
76+
try:
77+
current_clipboard = self._get_clipboard()
78+
79+
# Only update if clipboard content changed
80+
if current_clipboard != self.last_clipboard:
81+
self.last_clipboard = current_clipboard
82+
return current_clipboard
83+
except Exception as err:
84+
logger.debug("Error reading clipboard: %s", err)
85+
86+
return self.last_clipboard
87+
88+
def start_control(self, topic, data):
89+
"""Control system"""
90+
if topic[1] == "clipboard_set":
91+
try:
92+
self._set_clipboard(data)
93+
logger.info("Clipboard set successfully")
94+
except Exception as err:
95+
logger.error("Error setting clipboard: %s", err)
96+
97+
def _get_clipboard(self):
98+
"""Get current clipboard content"""
99+
if self.clipboard_tool == "wl-clipboard":
100+
# Use wl-paste for Wayland
101+
stdout, _, returncode = syscommand(
102+
"wl-paste --no-newline",
103+
ignore_errors=True,
104+
timeout=1,
105+
)
106+
if returncode == 0:
107+
return stdout
108+
109+
elif self.clipboard_tool == "xclip":
110+
# Use xclip for X11
111+
stdout, _, returncode = syscommand(
112+
"xclip -selection clipboard -o",
113+
ignore_errors=True,
114+
timeout=1,
115+
)
116+
if returncode == 0:
117+
return stdout
118+
119+
elif self.clipboard_tool == "xsel":
120+
# Use xsel for X11
121+
stdout, _, returncode = syscommand(
122+
"xsel --clipboard --output",
123+
ignore_errors=True,
124+
timeout=1,
125+
)
126+
if returncode == 0:
127+
return stdout
128+
129+
return ""
130+
131+
def _set_clipboard(self, text):
132+
"""Set clipboard content"""
133+
# Escape single quotes in text for shell command
134+
escaped_text = text.replace("'", "'\\''")
135+
136+
if self.clipboard_tool == "wl-clipboard":
137+
# Use wl-copy for Wayland
138+
_, _, returncode = syscommand(
139+
f"printf '%s' '{escaped_text}' | wl-copy",
140+
timeout=2,
141+
)
142+
if returncode != 0:
143+
raise SystemError(f"wl-copy failed with code {returncode}")
144+
145+
elif self.clipboard_tool == "xclip":
146+
# Use xclip for X11
147+
_, _, returncode = syscommand(
148+
f"printf '%s' '{escaped_text}' | xclip -selection clipboard",
149+
timeout=2,
150+
)
151+
if returncode != 0:
152+
raise SystemError(f"xclip failed with code {returncode}")
153+
154+
elif self.clipboard_tool == "xsel":
155+
# Use xsel for X11
156+
_, _, returncode = syscommand(
157+
f"printf '%s' '{escaped_text}' | xsel --clipboard --input",
158+
timeout=2,
159+
)
160+
if returncode != 0:
161+
raise SystemError(f"xsel failed with code {returncode}")
162+
163+
# Update last_clipboard to avoid triggering monitor on self-set
164+
self.last_clipboard = text

0 commit comments

Comments
 (0)