robotframework-parrot-touchcontroller 1.0.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,249 @@
1
+ from robot.api.deco import keyword
2
+ from parrot.touch.dummy import DummyTouch
3
+ from parrot.touch.axitouch import AxiDrawTouch
4
+ from parrot.touch.base import Error
5
+ from parrot.touch.adbtouch import ADBTouch
6
+ from parrot.touch.usbhidtouch import UsbHidTouch
7
+ from typing import Dict, Any
8
+
9
+ class TouchError(Exception):
10
+ """Raised when a device-level touch error occurs."""
11
+
12
+
13
+ class TouchController:
14
+ """
15
+ A Robot Framework library for simulating touch input across various
16
+ device types through a unified interface.
17
+
18
+ == Configuration ==
19
+
20
+ Touch devices are configured using the `Touch Setup` keyword with a dictionary
21
+ typically loaded from a YAML file (e.g. `config.yaml`) by importing the library
22
+ and variables into your test suite. Each entry in the configuration must include
23
+ a `type` field that specifies the device type. Supported types include:
24
+
25
+ - `UsbHidTouch`: USB HID-based touch simulation over TCP.
26
+ - `AxiDrawTouch`: Physical touch simulation using a pen plotter.
27
+ - `ADBTouch`: Android device touch simulation over ADB.
28
+
29
+ === UsbHidTouch ===
30
+ Simulates touch over a USB HID device exposed via a TCP server.
31
+ Supported configs:
32
+ - `host`: IP address or hostname of the USB HID server (e.g., `127.0.0.1`)
33
+ - `port`: TCP port number (e.g., `8000`)
34
+ - `width`, `height`: Touch coordinate resolution (e.g., 1024x600)
35
+
36
+ === AxiDrawTouch ===
37
+ Controls an AxiDraw pen plotter to simulate physical touchscreen input.
38
+ Supported configs:
39
+ - `device`: Serial device path (e.g., `/dev/ttyUSB0`)
40
+ - `width`, `height`: Touch surface resolution in pixels
41
+ - `start_pos`: Physical start coordinates (e.g., `[0, 0]`)
42
+ - `end_pos`: Physical end coordinates defining working area (e.g., `[8.66, 5.12]`)
43
+
44
+ === ADBTouch ===
45
+ Simulates touch events on an Android device using ADB commands.
46
+ Supported configs:
47
+ - `ip`: IP address of the Android device (e.g., `192.168.43.168`)
48
+
49
+ === Configuration Sample (config.yaml) ===
50
+
51
+ The configuration file is written in YAML format and contains a top-level key
52
+ `TouchConfig` with device definitions:
53
+
54
+ | TouchConfig:
55
+ | T1:
56
+ | type: UsbHidTouch
57
+ | host: 127.0.0.1
58
+ | port: 8000
59
+ | width: 1024
60
+ | height: 600
61
+ |
62
+ | T2:
63
+ | type: AxiDrawTouch
64
+ | device: /dev/ttyUSB0
65
+ | width: 1024
66
+ | height: 600
67
+ | start_pos: [0, 0]
68
+ | end_pos: [8.66, 5.12]
69
+ |
70
+ | T3:
71
+ | type: ADBTouch
72
+ | ip: 192.168.43.168
73
+
74
+ = General Usage =
75
+
76
+ Setup and initialize touch devices from a YAML configuration. Simulate touch input such as tap and swipe gestures
77
+ across multiple supported devices. This library supports both real physical systems and simulated touch environments,
78
+ making it suitable for automated device interaction and UI testing.
79
+
80
+ = Example =
81
+
82
+ | ***** Settings *****
83
+ | Library parrot.TouchController
84
+ | Variables config.yaml
85
+ |
86
+ | ***** Test Cases *****
87
+ | Run Touch Tests
88
+ | Touch Setup ${TouchConfig} # Initializes all touch devices from the YAML configuration.
89
+ | Tap T1 640 500 0.2 # Simulates a tap at (640, 500) for 0.2 seconds on T1.
90
+ | Swipe T1 100 100 600 400 0.2 # Simulates swipe from (100,100) to (600,400).
91
+ | Touch Command T1 move 0 1 # Moves the touch to specified screen coordinates.
92
+ | Touch Command T1 home # Returns the axitouch to its home position.
93
+
94
+ """
95
+
96
+
97
+ ROBOT_LIBRARY_SCOPE = 'GLOBAL'
98
+
99
+ def __init__(self) -> None:
100
+ self.devices: Dict[str, Any] = {}
101
+
102
+ @keyword("Touch Setup")
103
+ def setup(self, config: Dict[str, Dict[str, Any]]) -> None:
104
+ """Initializes and configures touch controller devices.
105
+ Loads controller instances from the given `Configuration` dictionary
106
+ and prepares them for input simulation.
107
+
108
+ *Syntax*
109
+
110
+ | `Touch Setup` | <CONFIG> |
111
+
112
+ *Examples*
113
+
114
+ | `Touch Setup` | ${TouchConfig} | # Loads and initializes all camera devices defined in the `${CameraConfig}` dictionary. |
115
+ """
116
+ if not isinstance(config, dict):
117
+ raise Error("Expected a dictionary of controller configurations.")
118
+
119
+ controller_classes: Dict[str, Any] = {
120
+ "DummyTouch": DummyTouch,
121
+ "AxiDrawTouch": AxiDrawTouch,
122
+ "ADBTouch": ADBTouch,
123
+ "UsbHidTouch": UsbHidTouch,
124
+ }
125
+
126
+ for name, dev in config.items():
127
+ class_type = dev.get("type")
128
+
129
+ if not class_type or not name:
130
+ raise Error("Each TouchController entry must have 'type' and 'name' fields.")
131
+
132
+ if class_type not in controller_classes:
133
+ raise Error(f"Controller class '{class_type}' is not supported.")
134
+
135
+ try:
136
+ device = controller_classes[class_type](dev)
137
+ device.touch_init()
138
+ except Error as err:
139
+ raise TouchError(f"Failed to initialize controller '{name}' of type '{class_type}': {err}") from err
140
+
141
+
142
+ self.devices[name] = device
143
+
144
+ print(f"[INFO] Loaded controllers: {list(self.devices.keys())}")
145
+
146
+ @keyword("Tap")
147
+ def send_touch(self, device: str, x: float, y: float, tap_time: float = 0.2) -> None:
148
+ """
149
+ Simulates a tap action on the given device at specified screen coordinates.
150
+ Requires the device name, X and Y coordinates in pixels, and an
151
+ optional tap duration (default: 0.2 seconds).
152
+
153
+ *Syntax*
154
+
155
+ | `Tap` | <DEVICE_NAME> | <X> | <Y> | <TAP_TIME> |
156
+
157
+ *Examples*
158
+
159
+ | `Tap` | T1 | 640 | 500 | 0.2 | # Simulates a tap at (640, 500) on device `T1` with a duration of 0.2 seconds. |
160
+
161
+ """
162
+ controller = self.devices.get(device)
163
+ if not controller:
164
+ raise TouchError(f"Touch controller '{device}' not found.")
165
+ controller.touch_tap(float(x), float(y), float(tap_time))
166
+
167
+ @keyword("Swipe")
168
+ def send_swipe(
169
+ self,
170
+ device: str,
171
+ x1: float,
172
+ y1: float,
173
+ x2: float,
174
+ y2: float,
175
+ ) -> None:
176
+ """
177
+ Simulates a swipe gesture on the given device from (x1, y1) to (x2, y2).
178
+ Requires the device name, starting and ending coordinates in pixels.
179
+
180
+ *Syntax*
181
+
182
+ | <DEVICE_NAME> | <DEVICE_NAME> | <X1> | <Y1> | <X2> | <Y2> |
183
+
184
+ *Examples*
185
+
186
+ | `Swipe` | T1 | 100 | 100 | 600 | 400 | 0.3 | # Simulates a swipe on device `T1` from (100, 100) to (600, 400) |
187
+
188
+ """
189
+
190
+ controller = self.devices.get(device)
191
+ if not controller:
192
+ raise TouchError(f"Touch controller '{device}' not found.")
193
+ controller.touch_swipe(float(x1), float(y1), float(x2), float(y2))
194
+
195
+ @keyword("Touch Command")
196
+ def command(self, device, command, *args):
197
+ """
198
+ Executes a backend-specific touch command on the given device controller.
199
+
200
+ This keyword is used to send a command (e.g., `Move`, `Home`) to a specific device.
201
+ The available commands and required arguments depends on the backend implementation
202
+ of the touch controller. Currently only the AxiDrawTouch backend has commands.
203
+
204
+ AxiDrawTouch backend supports the following commands.
205
+
206
+ - `Move` — Moves to the specified position (in inches) without touching.
207
+ Requires two arguments: `x`, `y`.
208
+ - `Home` — Returns the touch to its home (default) position.
209
+ Takes no arguments.
210
+
211
+ *Syntax*
212
+
213
+ | Touch Command | <DEVICE_NAME> | <COMMAND> | [<ARG1>] | [<ARG2>] |
214
+
215
+ *Examples*
216
+
217
+ | Touch Command | T1 | move | 200 | 300 |
218
+ | Touch Command | T1 | home |
219
+
220
+ """
221
+ controller = self.devices.get(device)
222
+ if not controller:
223
+ raise TouchError(f"Touch controller '{device}' not found.")
224
+ controller.touch_command(command, *args)
225
+
226
+
227
+ @keyword("Touch Set Param")
228
+ def set_param_auto_home(self, device: str, param: str, value: str) -> None:
229
+ """
230
+ Sets values to configure runtime parameters for Touch control devices.
231
+
232
+ Currently, this keyword supports only for `AxiDrawTouch` type devices,
233
+
234
+ | *Parameter* | *Description* | *Example Value* |
235
+ | `auto_home` | Specifies the touch action to be directed to the home position | "True or False" |
236
+
237
+ *Syntax*
238
+
239
+ | `Touch Set Param` | <DEVICE_NAME> | auto_home | <VALUE> |
240
+
241
+ *Examples*
242
+
243
+ | `Touch Set Param` | T1 | auto_home | True | # Enables auto-home on the touch controller device. |
244
+ | `Touch Set Param` | T2 | auto_home | False | # Disables auto-home on the touch controller device. |
245
+ """
246
+ controller = self.devices.get(device)
247
+ if not controller:
248
+ raise TouchError(f"Touch controller '{device}' not found.")
249
+ controller.set_param(param, value)
@@ -0,0 +1,84 @@
1
+ from parrot import ADBController
2
+ from parrot.touch.base import BaseTouch, Error
3
+
4
+ class ADBTouch(BaseTouch):
5
+ """
6
+ A touch controller implementation using ADB (Android Debug Bridge).
7
+
8
+ This class provides methods to simulate touch gestures such as tap and swipe
9
+ on Android devices over a network or USB using ADB shell commands.
10
+ """
11
+
12
+ def __init__(self, config: dict):
13
+ """
14
+ Initializes the ADB touch controller.
15
+
16
+ The configuration dictionary must contain:
17
+ - `ip`: IP address of the ADB-connected device
18
+
19
+ Raises:
20
+ Error: If required configuration is missing or initialization fails.
21
+ """
22
+ try:
23
+ self.device_ip = config["ip"]
24
+ self.adb = ADBController.ADBController()
25
+ except KeyError as err:
26
+ raise Error(f"Missing required configuration key: {err}") from err
27
+ except Exception as err:
28
+ raise Error(f"Initialization failed: {err}") from err
29
+
30
+ def touch_init(self):
31
+ """
32
+ Initializes the ADB device connection using the provided IP.
33
+
34
+ Raises:
35
+ Error: If the device connection fails.
36
+ """
37
+ try:
38
+ self.adb.adb_ip_connect(self.device_ip)
39
+ print(f"ADBTouch: Connected to device {self.device_ip}")
40
+ except Exception as err:
41
+ raise Error("Initialization failed") from err
42
+
43
+ def touch_tap(self, x, y, tap_time):
44
+ """
45
+ Simulates a tap at the given screen coordinates using ADB shell.
46
+
47
+ Args:
48
+ x (int | float): X-coordinate in pixels.
49
+ y (int | float): Y-coordinate in pixels.
50
+ tap_time: (unused) Compatibility with base method signature.
51
+
52
+ Raises:
53
+ Error: If the tap command fails.
54
+ """
55
+ try:
56
+ cmd = ["input", "tap", str(int(x)), str(int(y))]
57
+ self.adb.adb_shell(cmd)
58
+ print(f"ADBTouch: Tap at ({x}, {y})")
59
+ except Exception as err:
60
+ raise Error(f"Touch tap failed at ({x}, {y})") from err
61
+
62
+ def touch_swipe(self, startx, starty, endx, endy, swipe_time):
63
+ """
64
+ Simulates a swipe gesture from start to end coordinates using ADB shell.
65
+
66
+ Args:
67
+ startx (int | float): Start X-coordinate in pixels.
68
+ starty (int | float): Start Y-coordinate in pixels.
69
+ endx (int | float): End X-coordinate in pixels.
70
+ endy (int | float): End Y-coordinate in pixels.
71
+ swipe_time: (unused) Compatibility with base method signature.
72
+
73
+ Raises:
74
+ Error: If the swipe command fails.
75
+ """
76
+ try:
77
+ cmd = ["input", "swipe", str(int(startx)), str(int(starty)), str(int(endx)), str(int(endy))]
78
+ self.adb.adb_shell(cmd)
79
+ print(f"ADBTouch: Swipe from ({startx}, {starty}) to ({endx}, {endy})")
80
+ except Exception as err:
81
+ raise Error(f"Swipe failed from ({startx}, {starty}) to ({endx}, {endy})") from err
82
+
83
+ def touch_command(self, cmd, *args):
84
+ raise Error("Commands are not supported by the ADBTouch backend")
@@ -0,0 +1,185 @@
1
+ from robot.api.deco import keyword
2
+ from parrot.touch.base import BaseTouch,Error
3
+ from typing import Tuple, Any
4
+
5
+ import xmlrpc.client
6
+
7
+
8
+ class AxiTouchError(Exception):
9
+ """Represents an error in the AxiDrawTouch controller."""
10
+
11
+
12
+ class AxiDrawTouch(BaseTouch):
13
+ """A touch controller implementation using the AxiDraw plotting device.
14
+
15
+ This class provides methods to simulate touch gestures such as tap and swipe
16
+ by controlling an AxiDraw plotter based on screen coordinates.
17
+ """
18
+
19
+ PROPERTIES = ["auto_home"]
20
+
21
+ def __init__(self, config: dict[str, Any]) -> None:
22
+ """Initializes the AxiDraw touch controller.
23
+
24
+ The configuration dictionary must contain:
25
+ - `width`: screen width in pixels
26
+ - `height`: screen height in pixels
27
+ - `start_pos`: tuple (x, y) of starting cartesian coordinates (in inches)
28
+ - `end_pos`: tuple (x, y) of ending cartesian coordinates (in inches)
29
+
30
+ Raises:
31
+ AxiTouchError: If initialization or connection to AxiDraw fails.
32
+ """
33
+
34
+ try:
35
+ uri = f"http://{config['host']}:{config['port']}"
36
+ self.screen_width_px: int = config["width"]
37
+ self.screen_height_px: int = config["height"]
38
+ self.cartesian_start: Tuple[float, float] = config["start_pos"]
39
+ self.cartesian_end: Tuple[float, float] = config["end_pos"]
40
+
41
+ self.auto_home = False # Flag to track auto-home behavior
42
+
43
+ # Optional: configure pen heights and speed
44
+ pen_pos_up = 75
45
+ pen_pos_down = 40
46
+ speed_pendown = 100
47
+ speed_penup = 100
48
+ accel = 75
49
+
50
+ self.axi = xmlrpc.client.ServerProxy(uri)
51
+
52
+ self.axi.connect(accel,
53
+ pen_pos_up, pen_pos_down,
54
+ speed_penup, speed_pendown)
55
+
56
+ except KeyError as err:
57
+ raise Error(f"Missing required configuration key: {err}") from err
58
+ except Exception as err:
59
+ raise Error(f"Initialization failed: {err}") from err
60
+
61
+ def convert_to_inches(self, x: float, y: float) -> Tuple[float, float]:
62
+ x = float(x)
63
+ y = float(y)
64
+ sx, sy = self.cartesian_start
65
+ ex, ey = self.cartesian_end
66
+ x_scale = (ex - sx) / self.screen_width_px
67
+ y_scale = (ey - sy) / self.screen_height_px
68
+ return round(sx + x * x_scale, 3), round(sy + y * y_scale, 3)
69
+
70
+
71
+ def touch_init(self) -> None:
72
+ """Initialize the pen by lifting it up and moving to origin (0, 0).
73
+
74
+ Raises:
75
+ AxiTouchError: If initialization movement fails.
76
+ """
77
+
78
+ try:
79
+ self.axi.penup()
80
+ self.axi.home()
81
+ print("AxiDrawTouch: Initialized and moved to origin")
82
+ except Exception as err:
83
+ raise Error("Initialization failed") from err
84
+
85
+ def touch_tap(self, x: float, y: float, tap_time) -> None:
86
+ """Simulate a tap at the given screen coordinates.
87
+
88
+ Args:
89
+ x (int | float): X-coordinate in pixels.
90
+ y (int | float): Y-coordinate in pixels.
91
+ tap_time (float, optional): Duration to hold pen down. Defaults to 0.2s.
92
+
93
+ Raises:
94
+ AxiTouchError: If the tap operation fails.
95
+ """
96
+
97
+ try:
98
+ px, py = self.convert_to_inches(x, y)
99
+ print(px, py)
100
+ self.axi.tap(px, py, tap_time)
101
+ if self.auto_home:
102
+ self.axi.home()
103
+ print(f"AxiDrawTouch: Tap at ({x}, {y}) for {tap_time}s")
104
+ except Exception as err:
105
+ raise Error(f"Touch tap failed at ({x}, {y})") from err
106
+
107
+ def touch_swipe(
108
+ self,
109
+ startx: float,
110
+ starty: float,
111
+ endx: float,
112
+ endy: float
113
+ ) -> None:
114
+ """Simulate a swipe gesture from start to end coordinates.
115
+
116
+ Args:
117
+ startx (int | float): Start X-coordinate in pixels.
118
+ starty (int | float): Start Y-coordinate in pixels.
119
+ endx (int | float): End X-coordinate in pixels.
120
+ endy (int | float): End Y-coordinate in pixels.
121
+ swipe_time (float, optional): Duration of the swipe. Defaults to 0.2s.
122
+
123
+ Raises:
124
+ AxiTouchError: If the swipe operation fails.
125
+ """
126
+
127
+ try:
128
+ psx, psy = self.convert_to_inches(startx, starty)
129
+ pex, pey = self.convert_to_inches(endx, endy)
130
+ self.axi.swipe(psx, psy, pex, pey)
131
+ print(f"AxiDrawTouch: Swipe from ({startx}, {starty}) to ({endx}, {endy})")
132
+ except Exception as err:
133
+ raise Error(f"Swipe failed from ({startx}, {starty}) to ({endx}, {endy})") from err
134
+
135
+
136
+ def move(self, x: float, y: float):
137
+ """
138
+ Moving Axi arm to the absolute position in axi usable
139
+ area. where x and y are inches.
140
+ """
141
+ try:
142
+ if x == 0 and y == 0:
143
+ self.axi.home()
144
+ return
145
+ self.axi.move(x, y)
146
+ except Exception as err:
147
+ raise Error(f"Failed to Move pointing device from ({x}, {y})") from err
148
+
149
+ def home(self):
150
+ """Returns the touch to its home default position."""
151
+ try:
152
+ self.axi.home_with_limit_switch()
153
+ except Exception as err:
154
+ raise Error(f"Failed to Home pointing device") from err
155
+
156
+ def touch_command(self, cmd, *args):
157
+ """Executes a backend-specific touch command on the given device controller."""
158
+ cmd = cmd.lower()
159
+ if cmd == "move":
160
+ if len(args) != 2:
161
+ raise Error("move command expects x and y as argument")
162
+ x = float(args[0])
163
+ y = float(args[1])
164
+ self.move(x, y)
165
+ elif cmd == "home":
166
+ self.home()
167
+
168
+ else:
169
+ raise Error(f"Invalid command {cmd}")
170
+
171
+ def set_param(self, param: str, value: bool) -> None:
172
+ """
173
+ Set a runtime parameter for ArucoCapture.
174
+
175
+ | *Arguments* | *Description* |
176
+ | `param (str)` | Supported: `auto_home`. |
177
+ | `value (bool)` | True or False Value Supported. |
178
+
179
+ *Raises:*
180
+ | `Error` | If an unsupported parameter is provided. |
181
+ """
182
+ if param == 'auto_home' and param in self.PROPERTIES:
183
+ self.auto_home = value
184
+ else:
185
+ raise Error(f"ArucoCapture Error: Unsupported parameter: {param}")
parrot/touch/base.py ADDED
@@ -0,0 +1,32 @@
1
+ from abc import ABC, abstractmethod
2
+
3
+ class Error(Exception):
4
+ """Base error for all TouchController implementations."""
5
+ pass
6
+
7
+ class BaseTouch(ABC):
8
+ @abstractmethod
9
+ def touch_init(self) -> None:
10
+ """Initialize the touch controller."""
11
+ pass
12
+
13
+ @abstractmethod
14
+ def touch_tap(self, x: float, y: float, tap_time) -> None:
15
+ """Simulate a tap at (x, y) in pixels for the given duration."""
16
+ pass
17
+
18
+ @abstractmethod
19
+ def touch_swipe(
20
+ self,
21
+ startx: float,
22
+ starty: float,
23
+ endx: float,
24
+ endy: float,
25
+ tap_time
26
+ ) -> None:
27
+ """Simulate a swipe from (startx, starty) to (endx, endy) for the given duration."""
28
+ pass
29
+ @abstractmethod
30
+ def touch_command(self, cmd, *args) -> None:
31
+ """Executes a backend-specific touch command on the given device controller."""
32
+ pass
parrot/touch/dummy.py ADDED
@@ -0,0 +1,35 @@
1
+ from parrot.touch.base import BaseTouch
2
+ from typing import Any
3
+
4
+ class DummyTouch(BaseTouch):
5
+ """A dummy touch controller for testing purposes.
6
+
7
+ This class simulates touch interactions by printing output
8
+ instead of interacting with physical hardware.
9
+ """
10
+
11
+ def __init__(self, config: Any = None) -> None:
12
+ pass
13
+
14
+ def touch_init(self) -> None:
15
+ """Initialize the dummy touch system."""
16
+ print("DummyTouch: Initialized")
17
+
18
+ def touch_tap(self, x: float, y: float, tap_time) -> None:
19
+ """Simulate a tap gesture at the given coordinates."""
20
+ print(f"DummyTouch: Tap at ({x}, {y}) for {tap_time}s")
21
+
22
+ def touch_swipe(
23
+ self,
24
+ startx: float,
25
+ starty: float,
26
+ endx: float,
27
+ endy: float,
28
+ tap_time
29
+ ) -> None:
30
+ """Simulate a swipe gesture from start to end coordinates."""
31
+ print(f"DummyTouch: Swipe from ({startx}, {starty}) to ({endx}, {endy}) for {tap_time}s")
32
+
33
+ def touch_command(self, cmd, *args) -> None:
34
+ """Executes a backend-specific touch command on the given device controller."""
35
+ print(f"DummyTouch: Touch command {cmd}, {args}")
@@ -0,0 +1,120 @@
1
+ import xmlrpc.client
2
+ from typing import Any, Tuple
3
+ from parrot.touch.base import BaseTouch, Error
4
+
5
+
6
+ class UsbHidTouch(BaseTouch):
7
+ """
8
+ A touch controller implementation using USB-HID over XML-RPC.
9
+
10
+ This class allows simulating tap and swipe gestures by sending touch commands
11
+ over the network to a USB-HID device capable of interpreting XML-RPC messages.
12
+ """
13
+
14
+ def __init__(self, config: dict[str, Any]) -> None:
15
+ """
16
+ Initializes the USB-HID touch controller.
17
+
18
+ The configuration dictionary must contain:
19
+ - `host`: IP or hostname of the XML-RPC server.
20
+ - `port`: Port number of the server.
21
+ - `width`: Screen width in pixels.
22
+ - `height`: Screen height in pixels.
23
+
24
+ Raises:
25
+ Error: If any required configuration is missing or initialization fails.
26
+ """
27
+ try:
28
+ self.host: str = config["host"]
29
+ self.port: int = config["port"]
30
+ self.screen_width_px: int = config["width"]
31
+ self.screen_height_px: int = config["height"]
32
+ self.handler = None
33
+ except KeyError as err:
34
+ raise Error(f"Missing required configuration key: {err}") from err
35
+ except Exception as err:
36
+ raise Error(f"Initialization failed: {err}") from err
37
+
38
+ def touch_init(self) -> None:
39
+ """
40
+ Initializes the XML-RPC connection to the USB-HID server.
41
+
42
+ Raises:
43
+ Error: If the connection to the HID server fails.
44
+ """
45
+ try:
46
+ self.handler = xmlrpc.client.ServerProxy(f"http://{self.host}:{self.port}/")
47
+ print(f"UsbHidTouch: Connected to {self.host}:{self.port}")
48
+ except Exception as err:
49
+ raise Error(f"Failed to initialize USB-HID connection: {err}") from err
50
+
51
+ def _convert_to_hid_coordinates(self, x: float, y: float) -> Tuple[float, float]:
52
+ """
53
+ Converts pixel coordinates to USB-HID scaled coordinates (0 to 0.5).
54
+
55
+ Args:
56
+ x (float): X-coordinate in pixels.
57
+ y (float): Y-coordinate in pixels.
58
+
59
+ Returns:
60
+ Tuple[float, float]: Normalized HID coordinates.
61
+
62
+ Raises:
63
+ Error: If the conversion fails.
64
+ """
65
+ try:
66
+ x_point = (x / self.screen_width_px) * 0.5
67
+ y_point = (y / self.screen_height_px) * 0.5
68
+ return round(x_point, 6), round(y_point, 6)
69
+ except Exception as err:
70
+ raise Error(f"Failed to convert coordinates ({x}, {y})") from err
71
+
72
+ def touch_tap(self, x: float, y: float, tap_time) -> None:
73
+ """
74
+ Simulates a tap gesture at the given screen coordinates.
75
+
76
+ Args:
77
+ x (float): X-coordinate in pixels.
78
+ y (float): Y-coordinate in pixels.
79
+ tap_time: Duration to hold touch (passed for consistency, but used by remote endpoint).
80
+
81
+ Raises:
82
+ Error: If the tap operation fails or response is invalid.
83
+ """
84
+ try:
85
+ hx, hy = self._convert_to_hid_coordinates(x, y)
86
+ command = f"T,{hx},{hy},{hx},{hy},{tap_time}"
87
+ response = self.handler.process_data(command.encode()).data.decode().strip()
88
+ if response != 'EXECUTED':
89
+ raise Error(f"Unexpected response from HID device: {response}")
90
+ print(f"UsbHidTouch: Tap at ({x}, {y}) with response '{response}'")
91
+ except Exception as err:
92
+ raise Error(f"Touch tap failed at ({x}, {y})") from err
93
+
94
+ def touch_swipe(self, startx: float, starty: float, endx: float, endy: float, tap_time) -> None:
95
+ """
96
+ Simulates a swipe gesture from start to end coordinates.
97
+
98
+ Args:
99
+ startx (float): Start X-coordinate in pixels.
100
+ starty (float): Start Y-coordinate in pixels.
101
+ endx (float): End X-coordinate in pixels.
102
+ endy (float): End Y-coordinate in pixels.
103
+ tap_time: Duration of swipe (passed for consistency, but used by remote endpoint).
104
+
105
+ Raises:
106
+ Error: If the swipe operation fails or response is invalid.
107
+ """
108
+ try:
109
+ sx, sy = self._convert_to_hid_coordinates(startx, starty)
110
+ ex, ey = self._convert_to_hid_coordinates(endx, endy)
111
+ command = f"T,{sx},{sy},{ex},{ey},{tap_time}"
112
+ response = self.handler.process_data(command.encode()).data.decode().strip()
113
+ if response != 'EXECUTED':
114
+ raise Error(f"Unexpected response from HID device: {response}")
115
+ print(f"UsbHidTouch: Swipe from ({startx}, {starty}) to ({endx}, {endy}) with response '{response}'")
116
+ except Exception as err:
117
+ raise Error(f"Swipe failed from ({startx}, {starty}) to ({endx}, {endy})") from err
118
+
119
+ def touch_command(self, cmd, *args):
120
+ raise Error("Commands are not supported by the UsbHidTouch backend")
@@ -0,0 +1,34 @@
1
+ Metadata-Version: 2.4
2
+ Name: robotframework-parrot-touchcontroller
3
+ Version: 1.0.0
4
+ Summary: Touch control api to use within the Robot Framework ecosystem
5
+ Author-email: Zilogic Systems <code@zilogic.com>
6
+ License-Expression: Apache-2.0
7
+ Project-URL: Homepage, https://github.com/zilogic-systems/parrot
8
+ Project-URL: Repository, https://github.com//zilogic-systems/parrot.git
9
+ Project-URL: Issues, https://github.com/zilogic-systems/parrot/issues
10
+ Requires-Python: >=3.6
11
+ Description-Content-Type: text/markdown
12
+ Requires-Dist: robotframework
13
+ Requires-Dist: pyyaml
14
+ Requires-Dist: robotframework-parrot-adbcontroller
15
+
16
+ # Touch Controller
17
+
18
+ ## Installation
19
+
20
+ To install from package registry use the following command:
21
+
22
+ ```
23
+ $ pip install robotframework-parrot-touchcontroller
24
+ ```
25
+
26
+ To install from git repo use the following command:
27
+
28
+ ```
29
+ $ pip install .
30
+ ```
31
+
32
+ ## Example
33
+
34
+ Example of using the library in Robot Framework is available `example.robot`.
@@ -0,0 +1,10 @@
1
+ parrot/TouchController.py,sha256=I8XdsywwAckADREYkCebVBJyDyn-gydxQAY4dyQuk4g,9196
2
+ parrot/touch/adbtouch.py,sha256=F2L1rCqDhL_2H4vyTfaEb5VOdu88rNxvaJlm2-delAI,3116
3
+ parrot/touch/axitouch.py,sha256=XQN8jsZPbdUwRRoXNS5BeVtL5_UaGpidN37DuwEayHc,6396
4
+ parrot/touch/base.py,sha256=S4FkV02Xpfydgpoek2FAdl8mUsSnzPmnWV-JLBSnhgQ,891
5
+ parrot/touch/dummy.py,sha256=6yYigXIpt8qcbs-L9i8IlmI6SQoNaP2w_Iy2vg72o3g,1172
6
+ parrot/touch/usbhidtouch.py,sha256=4HE8BAxk1VdpXzLzzMf5qulqMgfg4hFjyxrBbXdKu90,4892
7
+ robotframework_parrot_touchcontroller-1.0.0.dist-info/METADATA,sha256=nv7DcM6gDVdQ0ABA4tZRYCZ2rljUb8LmglT8Si0h9fs,933
8
+ robotframework_parrot_touchcontroller-1.0.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
9
+ robotframework_parrot_touchcontroller-1.0.0.dist-info/top_level.txt,sha256=sdNEP7dgW_pa1UCcMwz5m7u-_kJz4nShrBgz76lX8yc,7
10
+ robotframework_parrot_touchcontroller-1.0.0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (80.9.0)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+