blob: c5c615f82a1e30c2bcca31e0b45fb30bdad1e38a [file] [log] [blame]
Joseph Hwang5d0dc642016-01-19 10:21:12 +08001# Copyright 2016 The Chromium OS Authors. All rights reserved.
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
5"""This module provides emulation of bluetooth HID devices."""
6
Joseph Hwangd3784cb2016-03-06 21:57:39 +08007import argparse
Joseph Hwang5d0dc642016-01-19 10:21:12 +08008import logging
9import sys
10import time
11
Yu-Hsuan Hsu04cff0e2020-02-13 11:55:10 +080012from .bluetooth_peripheral_kit import PeripheralKit
13from .bluetooth_rn42 import RN42
14from .bluetooth_rn42 import RN42Exception
Yoni Shavit886c5ae2019-04-04 18:11:54 -070015from chameleond.utils.bluetooth_nrf52 import nRF52
Joseph Hwang5d0dc642016-01-19 10:21:12 +080016
17class BluetoothHIDException(Exception):
18 """A dummpy exception class for Bluetooth HID class."""
19 pass
20
21
Alexander Lent7cbd1592017-08-08 20:12:02 -070022class BluetoothHID(object):
Joseph Hwang9f0e0242016-06-22 12:50:20 +080023 """A base bluetooth HID emulator class using RN-42 evaluation kit.
24
25 Note: every public member method should
26 return True or a non-None object if successful;
27 return False or Raise an exception otherwise.
28 """
Joseph Hwang5d0dc642016-01-19 10:21:12 +080029
Alexander Lentce006df2017-08-17 17:22:24 -070030 # TODO(josephsih): Find better way to use constants other than PeripheralKit
Joseph Hwangd13e5e72016-05-30 18:57:35 +080031 TMP_PIN_CODE = '0000' # A temporary pin code
Joseph Hwang5d0dc642016-01-19 10:21:12 +080032
33 SEND_DELAY_SECS = 0.2 # Need to sleep for a short while otherwise
34 # the bits may get lost during transmission.
Joseph Hwangd13e5e72016-05-30 18:57:35 +080035 INIT_SLEEP_SECS = 5 # Sleep after initialization for stabilization.
Joseph Hwang5d0dc642016-01-19 10:21:12 +080036
Alexander Lent7cbd1592017-08-08 20:12:02 -070037 def __init__(self, device_type, authentication_mode, kit_impl,
Joseph Hwang5d0dc642016-01-19 10:21:12 +080038 send_delay=SEND_DELAY_SECS):
39 """Initialization of BluetoothHID
40
41 Args:
42 device_type: the device type for emulation
43 authentication_mode: the authentication mode
Alexander Lent7cbd1592017-08-08 20:12:02 -070044 kit_impl: the implementation of a peripheral kit to be instantiated
Joseph Hwang5d0dc642016-01-19 10:21:12 +080045 send_delay: wait a while after sending data
46 """
Alexander Lent7cbd1592017-08-08 20:12:02 -070047 self._kit = kit_impl()
Joseph Hwang5d0dc642016-01-19 10:21:12 +080048 self.device_type = device_type
Joseph Hwangc1edeff2016-06-08 15:38:14 +080049 self.authentication_mode = authentication_mode
Joseph Hwang5d0dc642016-01-19 10:21:12 +080050 self.send_delay = send_delay
51
Alexander Lent980519b2017-08-25 18:38:07 -070052 # TODO(crbug.com/764055): Remove the use of __getattr__ after a refactor of
53 # this class to only expose kit APIs explicitly.
Alexander Lent7cbd1592017-08-08 20:12:02 -070054 def __getattr__(self, name):
55 """Gets the attribute of name from the owned peripheral kit instance
56
57 Allows calling methods (or getting attributes in general) on this class or
58 its subclasses that resolve to methods defined on the kit implementation.
59
60 Args:
61 name: The name of the attribute to be found.
62
63 Returns:
64 The attribute of the kit with given name, if it exists.
65 (This is the default behavior and kits should follow it.)
66
67 Raises:
68 AttributeError if the attribute is not found.
69 (This is the default behavior and kits should follow it.)
70 """
Alexander Lentd7356bc2017-09-11 20:16:18 -070071 if name.startswith("Mouse"):
72 error = "Kit API is not public. Use public API from BluetoothHIDMouse."
73 raise AttributeError(error)
Alexander Lent7cbd1592017-08-08 20:12:02 -070074 return getattr(self._kit, name)
75
Joseph Hwangd13e5e72016-05-30 18:57:35 +080076 def Init(self, factory_reset=True):
77 """Initialize the chip correctly.
Joseph Hwang5d0dc642016-01-19 10:21:12 +080078
Joseph Hwangd13e5e72016-05-30 18:57:35 +080079 Initialize the chip with proper HID register values.
Joseph Hwang5d0dc642016-01-19 10:21:12 +080080
Joseph Hwangd13e5e72016-05-30 18:57:35 +080081 Args:
82 factory_reset: True if a factory reset is needed.
83 False if we only want to reconnect the serial device.
84 """
85 # Create a new serial device every time since the serial driver
86 # on chameleon board is not very stable.
Alexander Lentce006df2017-08-17 17:22:24 -070087 result = self.CreateSerialDevice()
Joseph Hwang5d0dc642016-01-19 10:21:12 +080088
Joseph Hwangd13e5e72016-05-30 18:57:35 +080089 if factory_reset:
Alexander Lentbc790282017-08-23 15:04:12 -070090 # Enter command mode to issue commands.
91 # This must happen first, so that other commands work
Alexander Lentce006df2017-08-17 17:22:24 -070092 result = self.EnterCommandMode() and result
Alexander Lentbc790282017-08-23 15:04:12 -070093
Joseph Hwangd13e5e72016-05-30 18:57:35 +080094 # Do a factory reset to make sure it is in a known initial state.
95 # Do the factory reset before proceeding to set parameters below.
Alexander Lentce006df2017-08-17 17:22:24 -070096 result = self.FactoryReset() and result
Joseph Hwang5d0dc642016-01-19 10:21:12 +080097
Joseph Hwangd13e5e72016-05-30 18:57:35 +080098 # Set HID as the service profile.
Alexander Lentce006df2017-08-17 17:22:24 -070099 result = self.SetServiceProfileHID() and result
Joseph Hwang5d0dc642016-01-19 10:21:12 +0800100
Joseph Hwangd13e5e72016-05-30 18:57:35 +0800101 # Set the HID device type.
Alexander Lentce006df2017-08-17 17:22:24 -0700102 result = self.SetHIDType(self.device_type) and result
Joseph Hwang5d0dc642016-01-19 10:21:12 +0800103
Joseph Hwangd13e5e72016-05-30 18:57:35 +0800104 # Set the default class of service.
Alexander Lentce006df2017-08-17 17:22:24 -0700105 result = self.SetDefaultClassOfService() and result
Joseph Hwang5d0dc642016-01-19 10:21:12 +0800106
Joseph Hwangd13e5e72016-05-30 18:57:35 +0800107 # Set the class of device (CoD) according to the hid device type.
Alexander Lentce006df2017-08-17 17:22:24 -0700108 result = self.SetClassOfDevice(self.device_type) and result
Joseph Hwangd13e5e72016-05-30 18:57:35 +0800109
110 # Set authentication to the specified mode.
Yoni Shavit886c5ae2019-04-04 18:11:54 -0700111 if self.authentication_mode != PeripheralKit.OPEN_MODE:
112 result = self.SetAuthenticationMode(self.authentication_mode)\
113 and result
Joseph Hwangd13e5e72016-05-30 18:57:35 +0800114
115 # Set RN-42 to work as a slave.
Alexander Lentce006df2017-08-17 17:22:24 -0700116 result = self.SetSlaveMode() and result
Joseph Hwangd13e5e72016-05-30 18:57:35 +0800117
Alexander Lentedd8d562017-08-17 17:23:58 -0700118 # Set a temporary pin code for testing purpose.
119 # Only do this when we want to use a pin code.
120 if self.authentication_mode == PeripheralKit.PIN_CODE_MODE:
121 result = self.SetPinCode(self.TMP_PIN_CODE) and result
122
Joseph Hwangd13e5e72016-05-30 18:57:35 +0800123 # Enable the connection status message so that we could get the message
124 # of connection/disconnection status.
Alexander Lentce006df2017-08-17 17:22:24 -0700125 result = self.EnableConnectionStatusMessage() and result
Joseph Hwangd13e5e72016-05-30 18:57:35 +0800126
Yoni Shavit886c5ae2019-04-04 18:11:54 -0700127 if not isinstance(self._kit, nRF52):
128 # Reboot so that the configurations above take effect.
129 result = self.Reboot() and result
Joseph Hwangd13e5e72016-05-30 18:57:35 +0800130
Yoni Shavit886c5ae2019-04-04 18:11:54 -0700131 # Enter command mode again after reboot.
132 result = self.EnterCommandMode() and result
Joseph Hwangd13e5e72016-05-30 18:57:35 +0800133 time.sleep(self.INIT_SLEEP_SECS)
134
135 logging.info('A bluetooth HID "%s" device is connected.', self.device_type)
Alexander Lentce006df2017-08-17 17:22:24 -0700136 return result
Joseph Hwang5d0dc642016-01-19 10:21:12 +0800137
Joseph Hwang5d0dc642016-01-19 10:21:12 +0800138
139class BluetoothHIDKeyboard(BluetoothHID):
140 """A bluetooth HID keyboard emulator class."""
141
Alexander Lent7cbd1592017-08-08 20:12:02 -0700142 def __init__(self, authentication_mode, kit_impl):
Joseph Hwang5d0dc642016-01-19 10:21:12 +0800143 """Initialization of BluetoothHIDKeyboard
144
145 Args:
146 authentication_mode: the authentication mode
Alexander Lent7cbd1592017-08-08 20:12:02 -0700147 kit_impl: the implementation of a Bluetooth HID peripheral kit to use
Joseph Hwang5d0dc642016-01-19 10:21:12 +0800148 """
Alexander Lent7cbd1592017-08-08 20:12:02 -0700149 super(BluetoothHIDKeyboard, self).__init__(
Alexander Lentce006df2017-08-17 17:22:24 -0700150 PeripheralKit.KEYBOARD, authentication_mode, kit_impl)
Joseph Hwang5d0dc642016-01-19 10:21:12 +0800151
152 def Send(self, data):
153 """Send data to the remote host.
154
155 Args:
156 data: data to send to the remote host
Joseph Hwangcb5623b2016-02-17 18:56:34 +0800157 data could be either a string of printable ASCII characters or
158 a special key combination.
Joseph Hwang5d0dc642016-01-19 10:21:12 +0800159 """
160 # TODO(josephsih): should have a method to check the connection status.
161 # Currently, once RN-42 is connected to a remote host, all characters
162 # except chr(0) transmitted through the serial port are interpreted
163 # as characters to send to the remote host.
Joseph Hwang5d0dc642016-01-19 10:21:12 +0800164 logging.debug('HID device sending %r...', data)
165 self.SerialSendReceive(data, msg='BluetoothHID.Send')
166 time.sleep(self.send_delay)
167
Joseph Hwangcb5623b2016-02-17 18:56:34 +0800168 def SendKeyCombination(self, modifiers=None, keys=None):
169 """Send special key combinations to the remote host.
170
171 Args:
172 modifiers: a list of modifiers
173 keys: a list of scan codes of keys
174 """
175 press_codes = self.PressShorthandCodes(modifiers=modifiers, keys=keys)
176 release_codes = self.ReleaseShorthandCodes()
177 if press_codes and release_codes:
178 self.Send(press_codes)
179 self.Send(release_codes)
180 else:
181 logging.warn('modifers: %s and keys: %s are not valid', modifiers, keys)
182 return None
183
Joseph Hwang5d0dc642016-01-19 10:21:12 +0800184
Joseph Hwangd3784cb2016-03-06 21:57:39 +0800185class BluetoothHIDMouse(BluetoothHID):
186 """A bluetooth HID mouse emulator class."""
187
Alexander Lentd7356bc2017-09-11 20:16:18 -0700188 # Max and min values for HID mouse report values
189 HID_MAX_REPORT_VALUE = 127
190 HID_MIN_REPORT_VALUE = -127
Joseph Hwangd3784cb2016-03-06 21:57:39 +0800191
Alexander Lent7cbd1592017-08-08 20:12:02 -0700192 def __init__(self, authentication_mode, kit_impl):
Joseph Hwangd3784cb2016-03-06 21:57:39 +0800193 """Initialization of BluetoothHIDMouse
194
195 Args:
196 authentication_mode: the authentication mode
Alexander Lent7cbd1592017-08-08 20:12:02 -0700197 kit_impl: the implementation of a Bluetooth HID peripheral kit to use
Joseph Hwangd3784cb2016-03-06 21:57:39 +0800198 """
Alexander Lent7cbd1592017-08-08 20:12:02 -0700199 super(BluetoothHIDMouse, self).__init__(
Alexander Lentce006df2017-08-17 17:22:24 -0700200 PeripheralKit.MOUSE, authentication_mode, kit_impl)
Joseph Hwangd3784cb2016-03-06 21:57:39 +0800201
Alexander Lentd7356bc2017-09-11 20:16:18 -0700202 def _EnsureHIDValueInRange(self, value):
203 """Ensures given value is in the range [-127,127] (inclusive).
Joseph Hwangd3784cb2016-03-06 21:57:39 +0800204
205 Args:
Alexander Lentd7356bc2017-09-11 20:16:18 -0700206 value: The value that should be checked.
Joseph Hwangd3784cb2016-03-06 21:57:39 +0800207
Alexander Lentd7356bc2017-09-11 20:16:18 -0700208 Raises:
209 BluetoothHIDException if value is outside of the acceptable range.
210 """
211 if value < self.HID_MIN_REPORT_VALUE or value > self.HID_MAX_REPORT_VALUE:
212 error = "Value %s is outside of acceptable range [-127,127]." % value
213 logging.error(error)
214 raise BluetoothHIDException(error)
215
216 def Move(self, delta_x=0, delta_y=0):
217 """Move the mouse (delta_x, delta_y) steps.
218
219 If buttons are being pressed, they will stay pressed during this operation.
220 This move is relative to the current position by the HID standard.
221 Valid step values must be in the range [-127,127].
Joseph Hwangd3784cb2016-03-06 21:57:39 +0800222
223 Args:
Alexander Lentd7356bc2017-09-11 20:16:18 -0700224 delta_x: The number of steps to move horizontally.
225 Negative values move left, positive values move right.
226 delta_y: The number of steps to move vertically.
227 Negative values move up, positive values move down.
Joseph Hwangd3784cb2016-03-06 21:57:39 +0800228
Alexander Lentd7356bc2017-09-11 20:16:18 -0700229 Raises:
230 BluetoothHIDException if a given delta is not in [-127,127].
231 """
232 self._EnsureHIDValueInRange(delta_x)
233 self._EnsureHIDValueInRange(delta_y)
234 self._kit.MouseMove(delta_x, delta_y)
Joseph Hwangd3784cb2016-03-06 21:57:39 +0800235 time.sleep(self.send_delay)
236
Alexander Lentd7356bc2017-09-11 20:16:18 -0700237 def _PressLeftButton(self):
238 """Press the left button"""
239 self._kit.MousePressButtons({PeripheralKit.MOUSE_BUTTON_LEFT})
240 time.sleep(self.send_delay)
Joseph Hwangd3784cb2016-03-06 21:57:39 +0800241
Alexander Lentd7356bc2017-09-11 20:16:18 -0700242 def _PressRightButton(self):
243 """Press the right button"""
244 self._kit.MousePressButtons({PeripheralKit.MOUSE_BUTTON_RIGHT})
245 time.sleep(self.send_delay)
Joseph Hwangd3784cb2016-03-06 21:57:39 +0800246
Alexander Lentd7356bc2017-09-11 20:16:18 -0700247 def _ReleaseAllButtons(self):
248 """Press the right button"""
249 self._kit.MouseReleaseAllButtons()
250 time.sleep(self.send_delay)
Joseph Hwangd3784cb2016-03-06 21:57:39 +0800251
252 def LeftClick(self):
253 """Make a left click."""
Alexander Lentd7356bc2017-09-11 20:16:18 -0700254 self._PressLeftButton()
255 self._ReleaseAllButtons()
Joseph Hwangd3784cb2016-03-06 21:57:39 +0800256
257 def RightClick(self):
258 """Make a right click."""
Alexander Lentd7356bc2017-09-11 20:16:18 -0700259 self._PressRightButton()
260 self._ReleaseAllButtons()
Joseph Hwangd3784cb2016-03-06 21:57:39 +0800261
262 def ClickAndDrag(self, delta_x=0, delta_y=0):
Alexander Lentd7356bc2017-09-11 20:16:18 -0700263 """Left click, drag (delta_x, delta_y) steps, and release.
264
265 This move is relative to the current position by the HID standard.
266 Valid step values must be in the range [-127,127].
Joseph Hwangd3784cb2016-03-06 21:57:39 +0800267
268 Args:
Alexander Lentd7356bc2017-09-11 20:16:18 -0700269 delta_x: The number of steps to move horizontally.
270 Negative values move left, positive values move right.
271 delta_y: The number of steps to move vertically.
272 Negative values move up, positive values move down.
Joseph Hwangd3784cb2016-03-06 21:57:39 +0800273
Alexander Lentd7356bc2017-09-11 20:16:18 -0700274 Raises:
275 BluetoothHIDException if a given delta is not in [-127,127].
276 """
277 self._EnsureHIDValueInRange(delta_x)
278 self._EnsureHIDValueInRange(delta_y)
279 self._PressLeftButton()
280 self.Move(delta_x, delta_y)
281 self._ReleaseAllButtons()
282
283 def Scroll(self, steps):
284 """Scroll the mouse wheel steps number of steps.
285
286 Buttons currently pressed will stay pressed during this operation.
287 Valid step values must be in the range [-127,127].
Joseph Hwangd3784cb2016-03-06 21:57:39 +0800288
289 Args:
Alexander Lentd7356bc2017-09-11 20:16:18 -0700290 steps: The number of steps to scroll the wheel.
291 With traditional scrolling:
292 Negative values scroll down, positive values scroll up.
293 With reversed (formerly "Australian") scrolling this is reversed.
Joseph Hwangd3784cb2016-03-06 21:57:39 +0800294 """
Alexander Lentd7356bc2017-09-11 20:16:18 -0700295 self._EnsureHIDValueInRange(steps)
296 self._kit.MouseScroll(steps)
297 time.sleep(self.send_delay)
Joseph Hwang5d0dc642016-01-19 10:21:12 +0800298
299
300def DemoBluetoothHIDKeyboard(remote_address, chars):
301 """A simple demo of acting as a HID keyboard.
302
303 This simple demo works only after the HID device has already paired
304 with the remote device such that a link key has been exchanged. Then
305 the HID device could connect directly to the remote host without
306 pin code and sends the message.
307
308 A full flow would be letting a remote host pair with the HID device
309 with the pin code of the HID device. Thereafter, either the host or
310 the HID device could request to connect. This is out of the scope of
311 this simple demo.
312
313 Args:
314 remote_address: the bluetooth address of the target remote device
315 chars: the characters to send
316 """
Yu-Hsuan Hsu55b875e2020-02-12 10:49:17 +0800317 print('Creating an emulated bluetooth keyboard...')
Alexander Lentce006df2017-08-17 17:22:24 -0700318 # TODO(josephsih): Refactor test code to remove need for RN42 import
319 keyboard = BluetoothHIDKeyboard(PeripheralKit.PIN_CODE_MODE, RN42)
Joseph Hwangc1edeff2016-06-08 15:38:14 +0800320 keyboard.Init()
Joseph Hwang5d0dc642016-01-19 10:21:12 +0800321
Yu-Hsuan Hsu55b875e2020-02-12 10:49:17 +0800322 print('Connecting to the remote address %s...' % remote_address)
Joseph Hwang5d0dc642016-01-19 10:21:12 +0800323 try:
324 if keyboard.ConnectToRemoteAddress(remote_address):
Joseph Hwangcb5623b2016-02-17 18:56:34 +0800325 # Send printable ASCII strings a few times.
326 for i in range(1, 4):
Yu-Hsuan Hsu55b875e2020-02-12 10:49:17 +0800327 print('Sending "%s" for the %dth time...' % (chars, i))
Joseph Hwang5d0dc642016-01-19 10:21:12 +0800328 keyboard.Send(chars + ' ' + str(i))
Joseph Hwangcb5623b2016-02-17 18:56:34 +0800329
330 # Demo special key combinations below.
Yu-Hsuan Hsu55b875e2020-02-12 10:49:17 +0800331 print('Create a new chrome tab.')
Joseph Hwangcb5623b2016-02-17 18:56:34 +0800332 keyboard.SendKeyCombination(modifiers=[RN42.LEFT_CTRL],
333 keys=[RN42.SCAN_T])
334
Yu-Hsuan Hsu55b875e2020-02-12 10:49:17 +0800335 print('Navigate to Google page.')
Joseph Hwangcb5623b2016-02-17 18:56:34 +0800336 keyboard.Send('www.google.com')
337 time.sleep(1)
338
Yu-Hsuan Hsu55b875e2020-02-12 10:49:17 +0800339 print('Search hello world.')
Joseph Hwangcb5623b2016-02-17 18:56:34 +0800340 keyboard.Send('hello world')
341 time.sleep(1)
342
Yu-Hsuan Hsu55b875e2020-02-12 10:49:17 +0800343 print('Navigate back to the previous page.')
Joseph Hwangcb5623b2016-02-17 18:56:34 +0800344 keyboard.SendKeyCombination(keys=[RN42.SCAN_F1])
345 time.sleep(1)
346
Yu-Hsuan Hsu55b875e2020-02-12 10:49:17 +0800347 print('Switch to the previous tab.')
Joseph Hwangcb5623b2016-02-17 18:56:34 +0800348 keyboard.SendKeyCombination(modifiers=[RN42.LEFT_CTRL, RN42.LEFT_SHIFT],
349 keys=[RN42.SCAN_TAB])
Joseph Hwang5d0dc642016-01-19 10:21:12 +0800350 else:
Yu-Hsuan Hsu55b875e2020-02-12 10:49:17 +0800351 print('Something is wrong. Not able to connect to the remote address.')
352 print('Have you already paired RN-42 with the remote host?')
Joseph Hwang5d0dc642016-01-19 10:21:12 +0800353 finally:
Yu-Hsuan Hsu55b875e2020-02-12 10:49:17 +0800354 print('Disconnecting...')
Joseph Hwang5d0dc642016-01-19 10:21:12 +0800355 keyboard.Disconnect()
356
Yu-Hsuan Hsu55b875e2020-02-12 10:49:17 +0800357 print('Closing the keyboard...')
Joseph Hwang5d0dc642016-01-19 10:21:12 +0800358 keyboard.Close()
359
360
Joseph Hwangd3784cb2016-03-06 21:57:39 +0800361def DemoBluetoothHIDMouse(remote_address):
362 """A simple demo of acting as a HID mouse.
363
364 Args:
365 remote_address: the bluetooth address of the target remote device
366 """
Yu-Hsuan Hsu55b875e2020-02-12 10:49:17 +0800367 print('Creating an emulated bluetooth mouse...')
Alexander Lentce006df2017-08-17 17:22:24 -0700368 # TODO(josephsih): Refactor test code to remove need for RN42 import
369 mouse = BluetoothHIDMouse(PeripheralKit.PIN_CODE_MODE, RN42)
Joseph Hwangc1edeff2016-06-08 15:38:14 +0800370 mouse.Init()
Joseph Hwangd3784cb2016-03-06 21:57:39 +0800371
Joseph Hwang24ed76a2016-05-27 13:20:48 +0800372 connected = False
Yu-Hsuan Hsu55b875e2020-02-12 10:49:17 +0800373 print('Connecting to the remote address %s...' % remote_address)
Joseph Hwangd3784cb2016-03-06 21:57:39 +0800374 try:
375 if mouse.ConnectToRemoteAddress(remote_address):
Joseph Hwang24ed76a2016-05-27 13:20:48 +0800376 connected = True
377
Yu-Hsuan Hsu55b875e2020-02-12 10:49:17 +0800378 print('Click and drag horizontally.')
Joseph Hwangd3784cb2016-03-06 21:57:39 +0800379 mouse.ClickAndDrag(delta_x=100)
380 time.sleep(1)
381
Yu-Hsuan Hsu55b875e2020-02-12 10:49:17 +0800382 print('Make a right click.')
Joseph Hwangd3784cb2016-03-06 21:57:39 +0800383 mouse.RightClick()
384 time.sleep(1)
385
Yu-Hsuan Hsu55b875e2020-02-12 10:49:17 +0800386 print('Move the cursor upper left.')
Joseph Hwangd3784cb2016-03-06 21:57:39 +0800387 mouse.Move(delta_x=-30, delta_y=-40)
388 time.sleep(1)
389
Yu-Hsuan Hsu55b875e2020-02-12 10:49:17 +0800390 print('Make a left click.')
Joseph Hwangd3784cb2016-03-06 21:57:39 +0800391 mouse.LeftClick()
392 time.sleep(1)
393
Yu-Hsuan Hsu55b875e2020-02-12 10:49:17 +0800394 print('Move the cursor left.')
Joseph Hwangd3784cb2016-03-06 21:57:39 +0800395 mouse.Move(delta_x=-100)
396 time.sleep(1)
397
Yu-Hsuan Hsu55b875e2020-02-12 10:49:17 +0800398 print('Move the cursor up.')
Joseph Hwangd3784cb2016-03-06 21:57:39 +0800399 mouse.Move(delta_y=-90)
400 time.sleep(1)
401
Yu-Hsuan Hsu55b875e2020-02-12 10:49:17 +0800402 print('Move the cursor down right.')
Joseph Hwangd3784cb2016-03-06 21:57:39 +0800403 mouse.Move(delta_x=100, delta_y=90)
404 time.sleep(1)
405
Yu-Hsuan Hsu55b875e2020-02-12 10:49:17 +0800406 print('Scroll in one direction.')
Joseph Hwangd3784cb2016-03-06 21:57:39 +0800407 mouse.Scroll(-80)
408 time.sleep(1)
409
Yu-Hsuan Hsu55b875e2020-02-12 10:49:17 +0800410 print('Scroll in the opposite direction.')
Joseph Hwangd3784cb2016-03-06 21:57:39 +0800411 mouse.Scroll(100)
412 else:
Yu-Hsuan Hsu55b875e2020-02-12 10:49:17 +0800413 print('Something is wrong. Not able to connect to the remote address.')
Joseph Hwangd3784cb2016-03-06 21:57:39 +0800414 finally:
Joseph Hwang24ed76a2016-05-27 13:20:48 +0800415 if connected:
Yu-Hsuan Hsu55b875e2020-02-12 10:49:17 +0800416 print('Disconnecting...')
Joseph Hwang24ed76a2016-05-27 13:20:48 +0800417 try:
418 mouse.Disconnect()
Alexander Lentce006df2017-08-17 17:22:24 -0700419 # TODO(josephsih): Refactor test code to remove need for RN42 import
Joseph Hwang24ed76a2016-05-27 13:20:48 +0800420 except RN42Exception:
421 # RN-42 may have already disconnected.
422 pass
Joseph Hwangd3784cb2016-03-06 21:57:39 +0800423
Yu-Hsuan Hsu55b875e2020-02-12 10:49:17 +0800424 print('Closing the mouse...')
Joseph Hwangd3784cb2016-03-06 21:57:39 +0800425 mouse.Close()
426
427
428def _Parse():
429 """Parse the command line options."""
430 prog = sys.argv[0]
431 example_usage = ('Example:\n' +
432 ' python %s keyboard 00:11:22:33:44:55\n' % prog +
433 ' python %s mouse 00:11:22:33:44:55\n'% prog)
434 parser = argparse.ArgumentParser(
435 description='Emulate a HID device.\n' + example_usage,
436 formatter_class=argparse.RawTextHelpFormatter)
437 parser.add_argument('device',
438 choices=['keyboard', 'mouse'],
439 help='the device type to emulate')
440 parser.add_argument('remote_host_address',
441 help='the remote host address')
442 parser.add_argument('-c', '--chars_to_send',
443 default='echo hello world',
444 help='characters to send to the remote host')
445 args = parser.parse_args()
446
447 if len(args.remote_host_address.replace(':', '')) != 12:
Yu-Hsuan Hsu55b875e2020-02-12 10:49:17 +0800448 print('"%s" is not a valid bluetooth address.' % args.remote_host_address)
Joseph Hwangd3784cb2016-03-06 21:57:39 +0800449 exit(1)
450
Yu-Hsuan Hsu55b875e2020-02-12 10:49:17 +0800451 print('Emulate a %s and connect to remote host at %s' %
452 (args.device, args.remote_host_address))
Joseph Hwangd3784cb2016-03-06 21:57:39 +0800453 return args
454
455
456def Demo():
457 """Make demonstrations about how to use the HID emulation classes."""
458 args = _Parse()
459 device = args.device.lower()
460 if device == 'keyboard':
461 DemoBluetoothHIDKeyboard(args.remote_host_address, args.chars_to_send)
462 elif device == 'mouse':
463 DemoBluetoothHIDMouse(args.remote_host_address)
464 else:
465 args.print_help()
466
467
Joseph Hwang5d0dc642016-01-19 10:21:12 +0800468if __name__ == '__main__':
Joseph Hwangd3784cb2016-03-06 21:57:39 +0800469 Demo()