blob: e68ba4b4f0a39d595f6cdeee32f51a9a13863ee8 [file] [log] [blame]
Joseph Hwang2d426da2020-09-17 16:10:05 +08001# Lint as: python2, python3
Joseph Hwang5d0dc642016-01-19 10:21:12 +08002# Copyright 2016 The Chromium OS Authors. All rights reserved.
3# Use of this source code is governed by a BSD-style license that can be
4# found in the LICENSE file.
5
6"""This module provides emulation of bluetooth HID devices."""
7
Joseph Hwang2d426da2020-09-17 16:10:05 +08008from __future__ import absolute_import
9from __future__ import division
10from __future__ import print_function
11
Joseph Hwangd3784cb2016-03-06 21:57:39 +080012import argparse
Archie Pusaka3c9fd0c2021-04-13 15:26:46 +080013# TODO: to port chromite.lib.cros_logging to replace legacy logging
14import logging # pylint: disable=cros-logging-import
Joseph Hwang5d0dc642016-01-19 10:21:12 +080015import sys
16import time
17
Archie Pusaka3c9fd0c2021-04-13 15:26:46 +080018from six.moves import range
19from chameleond.utils.bluetooth_nrf52 import nRF52
Yu-Hsuan Hsu04cff0e2020-02-13 11:55:10 +080020from .bluetooth_peripheral_kit import PeripheralKit
21from .bluetooth_rn42 import RN42
22from .bluetooth_rn42 import RN42Exception
Joseph Hwang5d0dc642016-01-19 10:21:12 +080023
24class BluetoothHIDException(Exception):
25 """A dummpy exception class for Bluetooth HID class."""
Joseph Hwang5d0dc642016-01-19 10:21:12 +080026
27
Alexander Lent7cbd1592017-08-08 20:12:02 -070028class BluetoothHID(object):
Joseph Hwang9f0e0242016-06-22 12:50:20 +080029 """A base bluetooth HID emulator class using RN-42 evaluation kit.
30
31 Note: every public member method should
32 return True or a non-None object if successful;
33 return False or Raise an exception otherwise.
34 """
Joseph Hwang5d0dc642016-01-19 10:21:12 +080035
Alexander Lentce006df2017-08-17 17:22:24 -070036 # TODO(josephsih): Find better way to use constants other than PeripheralKit
Joseph Hwangd13e5e72016-05-30 18:57:35 +080037 TMP_PIN_CODE = '0000' # A temporary pin code
Joseph Hwang5d0dc642016-01-19 10:21:12 +080038
39 SEND_DELAY_SECS = 0.2 # Need to sleep for a short while otherwise
40 # the bits may get lost during transmission.
Joseph Hwangd13e5e72016-05-30 18:57:35 +080041 INIT_SLEEP_SECS = 5 # Sleep after initialization for stabilization.
Joseph Hwang5d0dc642016-01-19 10:21:12 +080042
Alexander Lent7cbd1592017-08-08 20:12:02 -070043 def __init__(self, device_type, authentication_mode, kit_impl,
Joseph Hwang5d0dc642016-01-19 10:21:12 +080044 send_delay=SEND_DELAY_SECS):
45 """Initialization of BluetoothHID
46
47 Args:
48 device_type: the device type for emulation
49 authentication_mode: the authentication mode
Alexander Lent7cbd1592017-08-08 20:12:02 -070050 kit_impl: the implementation of a peripheral kit to be instantiated
Joseph Hwang5d0dc642016-01-19 10:21:12 +080051 send_delay: wait a while after sending data
52 """
Alexander Lent7cbd1592017-08-08 20:12:02 -070053 self._kit = kit_impl()
Joseph Hwang5d0dc642016-01-19 10:21:12 +080054 self.device_type = device_type
Joseph Hwangc1edeff2016-06-08 15:38:14 +080055 self.authentication_mode = authentication_mode
Joseph Hwang5d0dc642016-01-19 10:21:12 +080056 self.send_delay = send_delay
57
Alexander Lent980519b2017-08-25 18:38:07 -070058 # TODO(crbug.com/764055): Remove the use of __getattr__ after a refactor of
59 # this class to only expose kit APIs explicitly.
Alexander Lent7cbd1592017-08-08 20:12:02 -070060 def __getattr__(self, name):
61 """Gets the attribute of name from the owned peripheral kit instance
62
63 Allows calling methods (or getting attributes in general) on this class or
64 its subclasses that resolve to methods defined on the kit implementation.
65
66 Args:
67 name: The name of the attribute to be found.
68
69 Returns:
70 The attribute of the kit with given name, if it exists.
71 (This is the default behavior and kits should follow it.)
72
73 Raises:
74 AttributeError if the attribute is not found.
75 (This is the default behavior and kits should follow it.)
76 """
Archie Pusaka3c9fd0c2021-04-13 15:26:46 +080077 if name.startswith('Mouse'):
78 error = 'Kit API is not public. Use public API from BluetoothHIDMouse.'
Alexander Lentd7356bc2017-09-11 20:16:18 -070079 raise AttributeError(error)
Alexander Lent7cbd1592017-08-08 20:12:02 -070080 return getattr(self._kit, name)
81
Joseph Hwangd13e5e72016-05-30 18:57:35 +080082 def Init(self, factory_reset=True):
83 """Initialize the chip correctly.
Joseph Hwang5d0dc642016-01-19 10:21:12 +080084
Joseph Hwangd13e5e72016-05-30 18:57:35 +080085 Initialize the chip with proper HID register values.
Joseph Hwang5d0dc642016-01-19 10:21:12 +080086
Joseph Hwangd13e5e72016-05-30 18:57:35 +080087 Args:
88 factory_reset: True if a factory reset is needed.
89 False if we only want to reconnect the serial device.
90 """
91 # Create a new serial device every time since the serial driver
92 # on chameleon board is not very stable.
Alexander Lentce006df2017-08-17 17:22:24 -070093 result = self.CreateSerialDevice()
Joseph Hwang5d0dc642016-01-19 10:21:12 +080094
Joseph Hwangd13e5e72016-05-30 18:57:35 +080095 if factory_reset:
Alexander Lentbc790282017-08-23 15:04:12 -070096 # Enter command mode to issue commands.
97 # This must happen first, so that other commands work
Alexander Lentce006df2017-08-17 17:22:24 -070098 result = self.EnterCommandMode() and result
Alexander Lentbc790282017-08-23 15:04:12 -070099
Joseph Hwangd13e5e72016-05-30 18:57:35 +0800100 # Do a factory reset to make sure it is in a known initial state.
101 # Do the factory reset before proceeding to set parameters below.
Alexander Lentce006df2017-08-17 17:22:24 -0700102 result = self.FactoryReset() and result
Joseph Hwang5d0dc642016-01-19 10:21:12 +0800103
Joseph Hwangd13e5e72016-05-30 18:57:35 +0800104 # Set HID as the service profile.
Alexander Lentce006df2017-08-17 17:22:24 -0700105 result = self.SetServiceProfileHID() and result
Joseph Hwang5d0dc642016-01-19 10:21:12 +0800106
Joseph Hwangd13e5e72016-05-30 18:57:35 +0800107 # Set the HID device type.
Alexander Lentce006df2017-08-17 17:22:24 -0700108 result = self.SetHIDType(self.device_type) and result
Joseph Hwang5d0dc642016-01-19 10:21:12 +0800109
Joseph Hwangd13e5e72016-05-30 18:57:35 +0800110 # Set the default class of service.
Alexander Lentce006df2017-08-17 17:22:24 -0700111 result = self.SetDefaultClassOfService() and result
Joseph Hwang5d0dc642016-01-19 10:21:12 +0800112
Joseph Hwangd13e5e72016-05-30 18:57:35 +0800113 # Set the class of device (CoD) according to the hid device type.
Alexander Lentce006df2017-08-17 17:22:24 -0700114 result = self.SetClassOfDevice(self.device_type) and result
Joseph Hwangd13e5e72016-05-30 18:57:35 +0800115
116 # Set authentication to the specified mode.
Yoni Shavit886c5ae2019-04-04 18:11:54 -0700117 if self.authentication_mode != PeripheralKit.OPEN_MODE:
118 result = self.SetAuthenticationMode(self.authentication_mode)\
119 and result
Joseph Hwangd13e5e72016-05-30 18:57:35 +0800120
Archie Pusaka8b872a72021-04-13 14:59:50 +0800121 # Set RN-42 to work as a peripheral.
122 result = self.SetPeripheralMode() and result
Joseph Hwangd13e5e72016-05-30 18:57:35 +0800123
Alexander Lentedd8d562017-08-17 17:23:58 -0700124 # Set a temporary pin code for testing purpose.
125 # Only do this when we want to use a pin code.
126 if self.authentication_mode == PeripheralKit.PIN_CODE_MODE:
127 result = self.SetPinCode(self.TMP_PIN_CODE) and result
128
Joseph Hwangd13e5e72016-05-30 18:57:35 +0800129 # Enable the connection status message so that we could get the message
130 # of connection/disconnection status.
Alexander Lentce006df2017-08-17 17:22:24 -0700131 result = self.EnableConnectionStatusMessage() and result
Joseph Hwangd13e5e72016-05-30 18:57:35 +0800132
Yoni Shavit886c5ae2019-04-04 18:11:54 -0700133 if not isinstance(self._kit, nRF52):
134 # Reboot so that the configurations above take effect.
135 result = self.Reboot() and result
Joseph Hwangd13e5e72016-05-30 18:57:35 +0800136
Yoni Shavit886c5ae2019-04-04 18:11:54 -0700137 # Enter command mode again after reboot.
138 result = self.EnterCommandMode() and result
Joseph Hwangd13e5e72016-05-30 18:57:35 +0800139 time.sleep(self.INIT_SLEEP_SECS)
140
141 logging.info('A bluetooth HID "%s" device is connected.', self.device_type)
Alexander Lentce006df2017-08-17 17:22:24 -0700142 return result
Joseph Hwang5d0dc642016-01-19 10:21:12 +0800143
Joseph Hwang5d0dc642016-01-19 10:21:12 +0800144
145class BluetoothHIDKeyboard(BluetoothHID):
146 """A bluetooth HID keyboard emulator class."""
147
Alexander Lent7cbd1592017-08-08 20:12:02 -0700148 def __init__(self, authentication_mode, kit_impl):
Joseph Hwang5d0dc642016-01-19 10:21:12 +0800149 """Initialization of BluetoothHIDKeyboard
150
151 Args:
152 authentication_mode: the authentication mode
Alexander Lent7cbd1592017-08-08 20:12:02 -0700153 kit_impl: the implementation of a Bluetooth HID peripheral kit to use
Joseph Hwang5d0dc642016-01-19 10:21:12 +0800154 """
Alexander Lent7cbd1592017-08-08 20:12:02 -0700155 super(BluetoothHIDKeyboard, self).__init__(
Alexander Lentce006df2017-08-17 17:22:24 -0700156 PeripheralKit.KEYBOARD, authentication_mode, kit_impl)
Joseph Hwang5d0dc642016-01-19 10:21:12 +0800157
158 def Send(self, data):
159 """Send data to the remote host.
160
161 Args:
162 data: data to send to the remote host
Joseph Hwangcb5623b2016-02-17 18:56:34 +0800163 data could be either a string of printable ASCII characters or
164 a special key combination.
Joseph Hwang5d0dc642016-01-19 10:21:12 +0800165 """
166 # TODO(josephsih): should have a method to check the connection status.
167 # Currently, once RN-42 is connected to a remote host, all characters
168 # except chr(0) transmitted through the serial port are interpreted
169 # as characters to send to the remote host.
Joseph Hwang5d0dc642016-01-19 10:21:12 +0800170 logging.debug('HID device sending %r...', data)
171 self.SerialSendReceive(data, msg='BluetoothHID.Send')
172 time.sleep(self.send_delay)
173
Joseph Hwangcb5623b2016-02-17 18:56:34 +0800174 def SendKeyCombination(self, modifiers=None, keys=None):
175 """Send special key combinations to the remote host.
176
177 Args:
178 modifiers: a list of modifiers
179 keys: a list of scan codes of keys
180 """
181 press_codes = self.PressShorthandCodes(modifiers=modifiers, keys=keys)
182 release_codes = self.ReleaseShorthandCodes()
183 if press_codes and release_codes:
184 self.Send(press_codes)
185 self.Send(release_codes)
186 else:
Archie Pusaka3c9fd0c2021-04-13 15:26:46 +0800187 logging.warning('modifers: %s and keys: %s are not valid',
188 modifiers, keys)
Joseph Hwangcb5623b2016-02-17 18:56:34 +0800189 return None
190
Joseph Hwang5d0dc642016-01-19 10:21:12 +0800191
Joseph Hwangd3784cb2016-03-06 21:57:39 +0800192class BluetoothHIDMouse(BluetoothHID):
193 """A bluetooth HID mouse emulator class."""
194
Alexander Lentd7356bc2017-09-11 20:16:18 -0700195 # Max and min values for HID mouse report values
196 HID_MAX_REPORT_VALUE = 127
197 HID_MIN_REPORT_VALUE = -127
Joseph Hwangd3784cb2016-03-06 21:57:39 +0800198
Alexander Lent7cbd1592017-08-08 20:12:02 -0700199 def __init__(self, authentication_mode, kit_impl):
Joseph Hwangd3784cb2016-03-06 21:57:39 +0800200 """Initialization of BluetoothHIDMouse
201
202 Args:
203 authentication_mode: the authentication mode
Alexander Lent7cbd1592017-08-08 20:12:02 -0700204 kit_impl: the implementation of a Bluetooth HID peripheral kit to use
Joseph Hwangd3784cb2016-03-06 21:57:39 +0800205 """
Alexander Lent7cbd1592017-08-08 20:12:02 -0700206 super(BluetoothHIDMouse, self).__init__(
Alexander Lentce006df2017-08-17 17:22:24 -0700207 PeripheralKit.MOUSE, authentication_mode, kit_impl)
Joseph Hwangd3784cb2016-03-06 21:57:39 +0800208
Alexander Lentd7356bc2017-09-11 20:16:18 -0700209 def _EnsureHIDValueInRange(self, value):
210 """Ensures given value is in the range [-127,127] (inclusive).
Joseph Hwangd3784cb2016-03-06 21:57:39 +0800211
212 Args:
Alexander Lentd7356bc2017-09-11 20:16:18 -0700213 value: The value that should be checked.
Joseph Hwangd3784cb2016-03-06 21:57:39 +0800214
Alexander Lentd7356bc2017-09-11 20:16:18 -0700215 Raises:
216 BluetoothHIDException if value is outside of the acceptable range.
217 """
218 if value < self.HID_MIN_REPORT_VALUE or value > self.HID_MAX_REPORT_VALUE:
Archie Pusaka3c9fd0c2021-04-13 15:26:46 +0800219 error = 'Value %s is outside of acceptable range [-127,127].' % value
Alexander Lentd7356bc2017-09-11 20:16:18 -0700220 logging.error(error)
221 raise BluetoothHIDException(error)
222
223 def Move(self, delta_x=0, delta_y=0):
224 """Move the mouse (delta_x, delta_y) steps.
225
226 If buttons are being pressed, they will stay pressed during this operation.
227 This move is relative to the current position by the HID standard.
228 Valid step values must be in the range [-127,127].
Joseph Hwangd3784cb2016-03-06 21:57:39 +0800229
230 Args:
Alexander Lentd7356bc2017-09-11 20:16:18 -0700231 delta_x: The number of steps to move horizontally.
232 Negative values move left, positive values move right.
233 delta_y: The number of steps to move vertically.
234 Negative values move up, positive values move down.
Joseph Hwangd3784cb2016-03-06 21:57:39 +0800235
Alexander Lentd7356bc2017-09-11 20:16:18 -0700236 Raises:
237 BluetoothHIDException if a given delta is not in [-127,127].
238 """
239 self._EnsureHIDValueInRange(delta_x)
240 self._EnsureHIDValueInRange(delta_y)
241 self._kit.MouseMove(delta_x, delta_y)
Joseph Hwangd3784cb2016-03-06 21:57:39 +0800242 time.sleep(self.send_delay)
243
Alexander Lentd7356bc2017-09-11 20:16:18 -0700244 def _PressLeftButton(self):
245 """Press the left button"""
246 self._kit.MousePressButtons({PeripheralKit.MOUSE_BUTTON_LEFT})
247 time.sleep(self.send_delay)
Joseph Hwangd3784cb2016-03-06 21:57:39 +0800248
Alexander Lentd7356bc2017-09-11 20:16:18 -0700249 def _PressRightButton(self):
250 """Press the right button"""
251 self._kit.MousePressButtons({PeripheralKit.MOUSE_BUTTON_RIGHT})
252 time.sleep(self.send_delay)
Joseph Hwangd3784cb2016-03-06 21:57:39 +0800253
Alexander Lentd7356bc2017-09-11 20:16:18 -0700254 def _ReleaseAllButtons(self):
255 """Press the right button"""
256 self._kit.MouseReleaseAllButtons()
257 time.sleep(self.send_delay)
Joseph Hwangd3784cb2016-03-06 21:57:39 +0800258
259 def LeftClick(self):
260 """Make a left click."""
Alexander Lentd7356bc2017-09-11 20:16:18 -0700261 self._PressLeftButton()
262 self._ReleaseAllButtons()
Joseph Hwangd3784cb2016-03-06 21:57:39 +0800263
264 def RightClick(self):
265 """Make a right click."""
Alexander Lentd7356bc2017-09-11 20:16:18 -0700266 self._PressRightButton()
267 self._ReleaseAllButtons()
Joseph Hwangd3784cb2016-03-06 21:57:39 +0800268
269 def ClickAndDrag(self, delta_x=0, delta_y=0):
Alexander Lentd7356bc2017-09-11 20:16:18 -0700270 """Left click, drag (delta_x, delta_y) steps, and release.
271
272 This move is relative to the current position by the HID standard.
273 Valid step values must be in the range [-127,127].
Joseph Hwangd3784cb2016-03-06 21:57:39 +0800274
275 Args:
Alexander Lentd7356bc2017-09-11 20:16:18 -0700276 delta_x: The number of steps to move horizontally.
277 Negative values move left, positive values move right.
278 delta_y: The number of steps to move vertically.
279 Negative values move up, positive values move down.
Joseph Hwangd3784cb2016-03-06 21:57:39 +0800280
Alexander Lentd7356bc2017-09-11 20:16:18 -0700281 Raises:
282 BluetoothHIDException if a given delta is not in [-127,127].
283 """
284 self._EnsureHIDValueInRange(delta_x)
285 self._EnsureHIDValueInRange(delta_y)
286 self._PressLeftButton()
287 self.Move(delta_x, delta_y)
288 self._ReleaseAllButtons()
289
290 def Scroll(self, steps):
291 """Scroll the mouse wheel steps number of steps.
292
293 Buttons currently pressed will stay pressed during this operation.
294 Valid step values must be in the range [-127,127].
Joseph Hwangd3784cb2016-03-06 21:57:39 +0800295
296 Args:
Alexander Lentd7356bc2017-09-11 20:16:18 -0700297 steps: The number of steps to scroll the wheel.
298 With traditional scrolling:
299 Negative values scroll down, positive values scroll up.
300 With reversed (formerly "Australian") scrolling this is reversed.
Joseph Hwangd3784cb2016-03-06 21:57:39 +0800301 """
Alexander Lentd7356bc2017-09-11 20:16:18 -0700302 self._EnsureHIDValueInRange(steps)
303 self._kit.MouseScroll(steps)
304 time.sleep(self.send_delay)
Joseph Hwang5d0dc642016-01-19 10:21:12 +0800305
306
307def DemoBluetoothHIDKeyboard(remote_address, chars):
308 """A simple demo of acting as a HID keyboard.
309
310 This simple demo works only after the HID device has already paired
311 with the remote device such that a link key has been exchanged. Then
312 the HID device could connect directly to the remote host without
313 pin code and sends the message.
314
315 A full flow would be letting a remote host pair with the HID device
316 with the pin code of the HID device. Thereafter, either the host or
317 the HID device could request to connect. This is out of the scope of
318 this simple demo.
319
320 Args:
321 remote_address: the bluetooth address of the target remote device
322 chars: the characters to send
323 """
Yu-Hsuan Hsu55b875e2020-02-12 10:49:17 +0800324 print('Creating an emulated bluetooth keyboard...')
Alexander Lentce006df2017-08-17 17:22:24 -0700325 # TODO(josephsih): Refactor test code to remove need for RN42 import
326 keyboard = BluetoothHIDKeyboard(PeripheralKit.PIN_CODE_MODE, RN42)
Joseph Hwangc1edeff2016-06-08 15:38:14 +0800327 keyboard.Init()
Joseph Hwang5d0dc642016-01-19 10:21:12 +0800328
Joseph Hwang2d426da2020-09-17 16:10:05 +0800329 print(('Connecting to the remote address %s...' % remote_address))
Joseph Hwang5d0dc642016-01-19 10:21:12 +0800330 try:
331 if keyboard.ConnectToRemoteAddress(remote_address):
Joseph Hwangcb5623b2016-02-17 18:56:34 +0800332 # Send printable ASCII strings a few times.
333 for i in range(1, 4):
Joseph Hwang2d426da2020-09-17 16:10:05 +0800334 print(('Sending "%s" for the %dth time...' % (chars, i)))
Joseph Hwang5d0dc642016-01-19 10:21:12 +0800335 keyboard.Send(chars + ' ' + str(i))
Joseph Hwangcb5623b2016-02-17 18:56:34 +0800336
337 # Demo special key combinations below.
Yu-Hsuan Hsu55b875e2020-02-12 10:49:17 +0800338 print('Create a new chrome tab.')
Joseph Hwangcb5623b2016-02-17 18:56:34 +0800339 keyboard.SendKeyCombination(modifiers=[RN42.LEFT_CTRL],
340 keys=[RN42.SCAN_T])
341
Yu-Hsuan Hsu55b875e2020-02-12 10:49:17 +0800342 print('Navigate to Google page.')
Joseph Hwangcb5623b2016-02-17 18:56:34 +0800343 keyboard.Send('www.google.com')
344 time.sleep(1)
345
Yu-Hsuan Hsu55b875e2020-02-12 10:49:17 +0800346 print('Search hello world.')
Joseph Hwangcb5623b2016-02-17 18:56:34 +0800347 keyboard.Send('hello world')
348 time.sleep(1)
349
Yu-Hsuan Hsu55b875e2020-02-12 10:49:17 +0800350 print('Navigate back to the previous page.')
Joseph Hwangcb5623b2016-02-17 18:56:34 +0800351 keyboard.SendKeyCombination(keys=[RN42.SCAN_F1])
352 time.sleep(1)
353
Yu-Hsuan Hsu55b875e2020-02-12 10:49:17 +0800354 print('Switch to the previous tab.')
Joseph Hwangcb5623b2016-02-17 18:56:34 +0800355 keyboard.SendKeyCombination(modifiers=[RN42.LEFT_CTRL, RN42.LEFT_SHIFT],
356 keys=[RN42.SCAN_TAB])
Joseph Hwang5d0dc642016-01-19 10:21:12 +0800357 else:
Yu-Hsuan Hsu55b875e2020-02-12 10:49:17 +0800358 print('Something is wrong. Not able to connect to the remote address.')
359 print('Have you already paired RN-42 with the remote host?')
Joseph Hwang5d0dc642016-01-19 10:21:12 +0800360 finally:
Yu-Hsuan Hsu55b875e2020-02-12 10:49:17 +0800361 print('Disconnecting...')
Joseph Hwang5d0dc642016-01-19 10:21:12 +0800362 keyboard.Disconnect()
363
Yu-Hsuan Hsu55b875e2020-02-12 10:49:17 +0800364 print('Closing the keyboard...')
Joseph Hwang5d0dc642016-01-19 10:21:12 +0800365 keyboard.Close()
366
367
Joseph Hwangd3784cb2016-03-06 21:57:39 +0800368def DemoBluetoothHIDMouse(remote_address):
369 """A simple demo of acting as a HID mouse.
370
371 Args:
372 remote_address: the bluetooth address of the target remote device
373 """
Yu-Hsuan Hsu55b875e2020-02-12 10:49:17 +0800374 print('Creating an emulated bluetooth mouse...')
Alexander Lentce006df2017-08-17 17:22:24 -0700375 # TODO(josephsih): Refactor test code to remove need for RN42 import
376 mouse = BluetoothHIDMouse(PeripheralKit.PIN_CODE_MODE, RN42)
Joseph Hwangc1edeff2016-06-08 15:38:14 +0800377 mouse.Init()
Joseph Hwangd3784cb2016-03-06 21:57:39 +0800378
Joseph Hwang24ed76a2016-05-27 13:20:48 +0800379 connected = False
Joseph Hwang2d426da2020-09-17 16:10:05 +0800380 print(('Connecting to the remote address %s...' % remote_address))
Joseph Hwangd3784cb2016-03-06 21:57:39 +0800381 try:
382 if mouse.ConnectToRemoteAddress(remote_address):
Joseph Hwang24ed76a2016-05-27 13:20:48 +0800383 connected = True
384
Yu-Hsuan Hsu55b875e2020-02-12 10:49:17 +0800385 print('Click and drag horizontally.')
Joseph Hwangd3784cb2016-03-06 21:57:39 +0800386 mouse.ClickAndDrag(delta_x=100)
387 time.sleep(1)
388
Yu-Hsuan Hsu55b875e2020-02-12 10:49:17 +0800389 print('Make a right click.')
Joseph Hwangd3784cb2016-03-06 21:57:39 +0800390 mouse.RightClick()
391 time.sleep(1)
392
Yu-Hsuan Hsu55b875e2020-02-12 10:49:17 +0800393 print('Move the cursor upper left.')
Joseph Hwangd3784cb2016-03-06 21:57:39 +0800394 mouse.Move(delta_x=-30, delta_y=-40)
395 time.sleep(1)
396
Yu-Hsuan Hsu55b875e2020-02-12 10:49:17 +0800397 print('Make a left click.')
Joseph Hwangd3784cb2016-03-06 21:57:39 +0800398 mouse.LeftClick()
399 time.sleep(1)
400
Yu-Hsuan Hsu55b875e2020-02-12 10:49:17 +0800401 print('Move the cursor left.')
Joseph Hwangd3784cb2016-03-06 21:57:39 +0800402 mouse.Move(delta_x=-100)
403 time.sleep(1)
404
Yu-Hsuan Hsu55b875e2020-02-12 10:49:17 +0800405 print('Move the cursor up.')
Joseph Hwangd3784cb2016-03-06 21:57:39 +0800406 mouse.Move(delta_y=-90)
407 time.sleep(1)
408
Yu-Hsuan Hsu55b875e2020-02-12 10:49:17 +0800409 print('Move the cursor down right.')
Joseph Hwangd3784cb2016-03-06 21:57:39 +0800410 mouse.Move(delta_x=100, delta_y=90)
411 time.sleep(1)
412
Yu-Hsuan Hsu55b875e2020-02-12 10:49:17 +0800413 print('Scroll in one direction.')
Joseph Hwangd3784cb2016-03-06 21:57:39 +0800414 mouse.Scroll(-80)
415 time.sleep(1)
416
Yu-Hsuan Hsu55b875e2020-02-12 10:49:17 +0800417 print('Scroll in the opposite direction.')
Joseph Hwangd3784cb2016-03-06 21:57:39 +0800418 mouse.Scroll(100)
419 else:
Yu-Hsuan Hsu55b875e2020-02-12 10:49:17 +0800420 print('Something is wrong. Not able to connect to the remote address.')
Joseph Hwangd3784cb2016-03-06 21:57:39 +0800421 finally:
Joseph Hwang24ed76a2016-05-27 13:20:48 +0800422 if connected:
Yu-Hsuan Hsu55b875e2020-02-12 10:49:17 +0800423 print('Disconnecting...')
Joseph Hwang24ed76a2016-05-27 13:20:48 +0800424 try:
425 mouse.Disconnect()
Alexander Lentce006df2017-08-17 17:22:24 -0700426 # TODO(josephsih): Refactor test code to remove need for RN42 import
Joseph Hwang24ed76a2016-05-27 13:20:48 +0800427 except RN42Exception:
428 # RN-42 may have already disconnected.
429 pass
Joseph Hwangd3784cb2016-03-06 21:57:39 +0800430
Yu-Hsuan Hsu55b875e2020-02-12 10:49:17 +0800431 print('Closing the mouse...')
Joseph Hwangd3784cb2016-03-06 21:57:39 +0800432 mouse.Close()
433
434
435def _Parse():
436 """Parse the command line options."""
437 prog = sys.argv[0]
438 example_usage = ('Example:\n' +
439 ' python %s keyboard 00:11:22:33:44:55\n' % prog +
440 ' python %s mouse 00:11:22:33:44:55\n'% prog)
441 parser = argparse.ArgumentParser(
442 description='Emulate a HID device.\n' + example_usage,
443 formatter_class=argparse.RawTextHelpFormatter)
444 parser.add_argument('device',
445 choices=['keyboard', 'mouse'],
446 help='the device type to emulate')
447 parser.add_argument('remote_host_address',
448 help='the remote host address')
449 parser.add_argument('-c', '--chars_to_send',
450 default='echo hello world',
451 help='characters to send to the remote host')
452 args = parser.parse_args()
453
454 if len(args.remote_host_address.replace(':', '')) != 12:
Joseph Hwang2d426da2020-09-17 16:10:05 +0800455 print(('"%s" is not a valid bluetooth address.' % args.remote_host_address))
Archie Pusaka3c9fd0c2021-04-13 15:26:46 +0800456 sys.exit(1)
Joseph Hwangd3784cb2016-03-06 21:57:39 +0800457
Joseph Hwang2d426da2020-09-17 16:10:05 +0800458 print(('Emulate a %s and connect to remote host at %s' %
459 (args.device, args.remote_host_address)))
Joseph Hwangd3784cb2016-03-06 21:57:39 +0800460 return args
461
462
463def Demo():
464 """Make demonstrations about how to use the HID emulation classes."""
465 args = _Parse()
466 device = args.device.lower()
467 if device == 'keyboard':
468 DemoBluetoothHIDKeyboard(args.remote_host_address, args.chars_to_send)
469 elif device == 'mouse':
470 DemoBluetoothHIDMouse(args.remote_host_address)
471 else:
472 args.print_help()
473
474
Joseph Hwang5d0dc642016-01-19 10:21:12 +0800475if __name__ == '__main__':
Joseph Hwangd3784cb2016-03-06 21:57:39 +0800476 Demo()