blob: 160726f3edbb16c59d27e3d2273e046e48fcc72d [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
Joseph Hwang5d0dc642016-01-19 10:21:12 +080013import logging
14import sys
15import time
16
Yu-Hsuan Hsu04cff0e2020-02-13 11:55:10 +080017from .bluetooth_peripheral_kit import PeripheralKit
18from .bluetooth_rn42 import RN42
19from .bluetooth_rn42 import RN42Exception
Yoni Shavit886c5ae2019-04-04 18:11:54 -070020from chameleond.utils.bluetooth_nrf52 import nRF52
Joseph Hwang2d426da2020-09-17 16:10:05 +080021from six.moves import range
Joseph Hwang5d0dc642016-01-19 10:21:12 +080022
23class BluetoothHIDException(Exception):
24 """A dummpy exception class for Bluetooth HID class."""
25 pass
26
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 """
Alexander Lentd7356bc2017-09-11 20:16:18 -070077 if name.startswith("Mouse"):
78 error = "Kit API is not public. Use public API from BluetoothHIDMouse."
79 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
121 # Set RN-42 to work as a slave.
Alexander Lentce006df2017-08-17 17:22:24 -0700122 result = self.SetSlaveMode() 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:
187 logging.warn('modifers: %s and keys: %s are not valid', modifiers, keys)
188 return None
189
Joseph Hwang5d0dc642016-01-19 10:21:12 +0800190
Joseph Hwangd3784cb2016-03-06 21:57:39 +0800191class BluetoothHIDMouse(BluetoothHID):
192 """A bluetooth HID mouse emulator class."""
193
Alexander Lentd7356bc2017-09-11 20:16:18 -0700194 # Max and min values for HID mouse report values
195 HID_MAX_REPORT_VALUE = 127
196 HID_MIN_REPORT_VALUE = -127
Joseph Hwangd3784cb2016-03-06 21:57:39 +0800197
Alexander Lent7cbd1592017-08-08 20:12:02 -0700198 def __init__(self, authentication_mode, kit_impl):
Joseph Hwangd3784cb2016-03-06 21:57:39 +0800199 """Initialization of BluetoothHIDMouse
200
201 Args:
202 authentication_mode: the authentication mode
Alexander Lent7cbd1592017-08-08 20:12:02 -0700203 kit_impl: the implementation of a Bluetooth HID peripheral kit to use
Joseph Hwangd3784cb2016-03-06 21:57:39 +0800204 """
Alexander Lent7cbd1592017-08-08 20:12:02 -0700205 super(BluetoothHIDMouse, self).__init__(
Alexander Lentce006df2017-08-17 17:22:24 -0700206 PeripheralKit.MOUSE, authentication_mode, kit_impl)
Joseph Hwangd3784cb2016-03-06 21:57:39 +0800207
Alexander Lentd7356bc2017-09-11 20:16:18 -0700208 def _EnsureHIDValueInRange(self, value):
209 """Ensures given value is in the range [-127,127] (inclusive).
Joseph Hwangd3784cb2016-03-06 21:57:39 +0800210
211 Args:
Alexander Lentd7356bc2017-09-11 20:16:18 -0700212 value: The value that should be checked.
Joseph Hwangd3784cb2016-03-06 21:57:39 +0800213
Alexander Lentd7356bc2017-09-11 20:16:18 -0700214 Raises:
215 BluetoothHIDException if value is outside of the acceptable range.
216 """
217 if value < self.HID_MIN_REPORT_VALUE or value > self.HID_MAX_REPORT_VALUE:
218 error = "Value %s is outside of acceptable range [-127,127]." % value
219 logging.error(error)
220 raise BluetoothHIDException(error)
221
222 def Move(self, delta_x=0, delta_y=0):
223 """Move the mouse (delta_x, delta_y) steps.
224
225 If buttons are being pressed, they will stay pressed during this operation.
226 This move is relative to the current position by the HID standard.
227 Valid step values must be in the range [-127,127].
Joseph Hwangd3784cb2016-03-06 21:57:39 +0800228
229 Args:
Alexander Lentd7356bc2017-09-11 20:16:18 -0700230 delta_x: The number of steps to move horizontally.
231 Negative values move left, positive values move right.
232 delta_y: The number of steps to move vertically.
233 Negative values move up, positive values move down.
Joseph Hwangd3784cb2016-03-06 21:57:39 +0800234
Alexander Lentd7356bc2017-09-11 20:16:18 -0700235 Raises:
236 BluetoothHIDException if a given delta is not in [-127,127].
237 """
238 self._EnsureHIDValueInRange(delta_x)
239 self._EnsureHIDValueInRange(delta_y)
240 self._kit.MouseMove(delta_x, delta_y)
Joseph Hwangd3784cb2016-03-06 21:57:39 +0800241 time.sleep(self.send_delay)
242
Alexander Lentd7356bc2017-09-11 20:16:18 -0700243 def _PressLeftButton(self):
244 """Press the left button"""
245 self._kit.MousePressButtons({PeripheralKit.MOUSE_BUTTON_LEFT})
246 time.sleep(self.send_delay)
Joseph Hwangd3784cb2016-03-06 21:57:39 +0800247
Alexander Lentd7356bc2017-09-11 20:16:18 -0700248 def _PressRightButton(self):
249 """Press the right button"""
250 self._kit.MousePressButtons({PeripheralKit.MOUSE_BUTTON_RIGHT})
251 time.sleep(self.send_delay)
Joseph Hwangd3784cb2016-03-06 21:57:39 +0800252
Alexander Lentd7356bc2017-09-11 20:16:18 -0700253 def _ReleaseAllButtons(self):
254 """Press the right button"""
255 self._kit.MouseReleaseAllButtons()
256 time.sleep(self.send_delay)
Joseph Hwangd3784cb2016-03-06 21:57:39 +0800257
258 def LeftClick(self):
259 """Make a left click."""
Alexander Lentd7356bc2017-09-11 20:16:18 -0700260 self._PressLeftButton()
261 self._ReleaseAllButtons()
Joseph Hwangd3784cb2016-03-06 21:57:39 +0800262
263 def RightClick(self):
264 """Make a right click."""
Alexander Lentd7356bc2017-09-11 20:16:18 -0700265 self._PressRightButton()
266 self._ReleaseAllButtons()
Joseph Hwangd3784cb2016-03-06 21:57:39 +0800267
268 def ClickAndDrag(self, delta_x=0, delta_y=0):
Alexander Lentd7356bc2017-09-11 20:16:18 -0700269 """Left click, drag (delta_x, delta_y) steps, and release.
270
271 This move is relative to the current position by the HID standard.
272 Valid step values must be in the range [-127,127].
Joseph Hwangd3784cb2016-03-06 21:57:39 +0800273
274 Args:
Alexander Lentd7356bc2017-09-11 20:16:18 -0700275 delta_x: The number of steps to move horizontally.
276 Negative values move left, positive values move right.
277 delta_y: The number of steps to move vertically.
278 Negative values move up, positive values move down.
Joseph Hwangd3784cb2016-03-06 21:57:39 +0800279
Alexander Lentd7356bc2017-09-11 20:16:18 -0700280 Raises:
281 BluetoothHIDException if a given delta is not in [-127,127].
282 """
283 self._EnsureHIDValueInRange(delta_x)
284 self._EnsureHIDValueInRange(delta_y)
285 self._PressLeftButton()
286 self.Move(delta_x, delta_y)
287 self._ReleaseAllButtons()
288
289 def Scroll(self, steps):
290 """Scroll the mouse wheel steps number of steps.
291
292 Buttons currently pressed will stay pressed during this operation.
293 Valid step values must be in the range [-127,127].
Joseph Hwangd3784cb2016-03-06 21:57:39 +0800294
295 Args:
Alexander Lentd7356bc2017-09-11 20:16:18 -0700296 steps: The number of steps to scroll the wheel.
297 With traditional scrolling:
298 Negative values scroll down, positive values scroll up.
299 With reversed (formerly "Australian") scrolling this is reversed.
Joseph Hwangd3784cb2016-03-06 21:57:39 +0800300 """
Alexander Lentd7356bc2017-09-11 20:16:18 -0700301 self._EnsureHIDValueInRange(steps)
302 self._kit.MouseScroll(steps)
303 time.sleep(self.send_delay)
Joseph Hwang5d0dc642016-01-19 10:21:12 +0800304
305
306def DemoBluetoothHIDKeyboard(remote_address, chars):
307 """A simple demo of acting as a HID keyboard.
308
309 This simple demo works only after the HID device has already paired
310 with the remote device such that a link key has been exchanged. Then
311 the HID device could connect directly to the remote host without
312 pin code and sends the message.
313
314 A full flow would be letting a remote host pair with the HID device
315 with the pin code of the HID device. Thereafter, either the host or
316 the HID device could request to connect. This is out of the scope of
317 this simple demo.
318
319 Args:
320 remote_address: the bluetooth address of the target remote device
321 chars: the characters to send
322 """
Yu-Hsuan Hsu55b875e2020-02-12 10:49:17 +0800323 print('Creating an emulated bluetooth keyboard...')
Alexander Lentce006df2017-08-17 17:22:24 -0700324 # TODO(josephsih): Refactor test code to remove need for RN42 import
325 keyboard = BluetoothHIDKeyboard(PeripheralKit.PIN_CODE_MODE, RN42)
Joseph Hwangc1edeff2016-06-08 15:38:14 +0800326 keyboard.Init()
Joseph Hwang5d0dc642016-01-19 10:21:12 +0800327
Joseph Hwang2d426da2020-09-17 16:10:05 +0800328 print(('Connecting to the remote address %s...' % remote_address))
Joseph Hwang5d0dc642016-01-19 10:21:12 +0800329 try:
330 if keyboard.ConnectToRemoteAddress(remote_address):
Joseph Hwangcb5623b2016-02-17 18:56:34 +0800331 # Send printable ASCII strings a few times.
332 for i in range(1, 4):
Joseph Hwang2d426da2020-09-17 16:10:05 +0800333 print(('Sending "%s" for the %dth time...' % (chars, i)))
Joseph Hwang5d0dc642016-01-19 10:21:12 +0800334 keyboard.Send(chars + ' ' + str(i))
Joseph Hwangcb5623b2016-02-17 18:56:34 +0800335
336 # Demo special key combinations below.
Yu-Hsuan Hsu55b875e2020-02-12 10:49:17 +0800337 print('Create a new chrome tab.')
Joseph Hwangcb5623b2016-02-17 18:56:34 +0800338 keyboard.SendKeyCombination(modifiers=[RN42.LEFT_CTRL],
339 keys=[RN42.SCAN_T])
340
Yu-Hsuan Hsu55b875e2020-02-12 10:49:17 +0800341 print('Navigate to Google page.')
Joseph Hwangcb5623b2016-02-17 18:56:34 +0800342 keyboard.Send('www.google.com')
343 time.sleep(1)
344
Yu-Hsuan Hsu55b875e2020-02-12 10:49:17 +0800345 print('Search hello world.')
Joseph Hwangcb5623b2016-02-17 18:56:34 +0800346 keyboard.Send('hello world')
347 time.sleep(1)
348
Yu-Hsuan Hsu55b875e2020-02-12 10:49:17 +0800349 print('Navigate back to the previous page.')
Joseph Hwangcb5623b2016-02-17 18:56:34 +0800350 keyboard.SendKeyCombination(keys=[RN42.SCAN_F1])
351 time.sleep(1)
352
Yu-Hsuan Hsu55b875e2020-02-12 10:49:17 +0800353 print('Switch to the previous tab.')
Joseph Hwangcb5623b2016-02-17 18:56:34 +0800354 keyboard.SendKeyCombination(modifiers=[RN42.LEFT_CTRL, RN42.LEFT_SHIFT],
355 keys=[RN42.SCAN_TAB])
Joseph Hwang5d0dc642016-01-19 10:21:12 +0800356 else:
Yu-Hsuan Hsu55b875e2020-02-12 10:49:17 +0800357 print('Something is wrong. Not able to connect to the remote address.')
358 print('Have you already paired RN-42 with the remote host?')
Joseph Hwang5d0dc642016-01-19 10:21:12 +0800359 finally:
Yu-Hsuan Hsu55b875e2020-02-12 10:49:17 +0800360 print('Disconnecting...')
Joseph Hwang5d0dc642016-01-19 10:21:12 +0800361 keyboard.Disconnect()
362
Yu-Hsuan Hsu55b875e2020-02-12 10:49:17 +0800363 print('Closing the keyboard...')
Joseph Hwang5d0dc642016-01-19 10:21:12 +0800364 keyboard.Close()
365
366
Joseph Hwangd3784cb2016-03-06 21:57:39 +0800367def DemoBluetoothHIDMouse(remote_address):
368 """A simple demo of acting as a HID mouse.
369
370 Args:
371 remote_address: the bluetooth address of the target remote device
372 """
Yu-Hsuan Hsu55b875e2020-02-12 10:49:17 +0800373 print('Creating an emulated bluetooth mouse...')
Alexander Lentce006df2017-08-17 17:22:24 -0700374 # TODO(josephsih): Refactor test code to remove need for RN42 import
375 mouse = BluetoothHIDMouse(PeripheralKit.PIN_CODE_MODE, RN42)
Joseph Hwangc1edeff2016-06-08 15:38:14 +0800376 mouse.Init()
Joseph Hwangd3784cb2016-03-06 21:57:39 +0800377
Joseph Hwang24ed76a2016-05-27 13:20:48 +0800378 connected = False
Joseph Hwang2d426da2020-09-17 16:10:05 +0800379 print(('Connecting to the remote address %s...' % remote_address))
Joseph Hwangd3784cb2016-03-06 21:57:39 +0800380 try:
381 if mouse.ConnectToRemoteAddress(remote_address):
Joseph Hwang24ed76a2016-05-27 13:20:48 +0800382 connected = True
383
Yu-Hsuan Hsu55b875e2020-02-12 10:49:17 +0800384 print('Click and drag horizontally.')
Joseph Hwangd3784cb2016-03-06 21:57:39 +0800385 mouse.ClickAndDrag(delta_x=100)
386 time.sleep(1)
387
Yu-Hsuan Hsu55b875e2020-02-12 10:49:17 +0800388 print('Make a right click.')
Joseph Hwangd3784cb2016-03-06 21:57:39 +0800389 mouse.RightClick()
390 time.sleep(1)
391
Yu-Hsuan Hsu55b875e2020-02-12 10:49:17 +0800392 print('Move the cursor upper left.')
Joseph Hwangd3784cb2016-03-06 21:57:39 +0800393 mouse.Move(delta_x=-30, delta_y=-40)
394 time.sleep(1)
395
Yu-Hsuan Hsu55b875e2020-02-12 10:49:17 +0800396 print('Make a left click.')
Joseph Hwangd3784cb2016-03-06 21:57:39 +0800397 mouse.LeftClick()
398 time.sleep(1)
399
Yu-Hsuan Hsu55b875e2020-02-12 10:49:17 +0800400 print('Move the cursor left.')
Joseph Hwangd3784cb2016-03-06 21:57:39 +0800401 mouse.Move(delta_x=-100)
402 time.sleep(1)
403
Yu-Hsuan Hsu55b875e2020-02-12 10:49:17 +0800404 print('Move the cursor up.')
Joseph Hwangd3784cb2016-03-06 21:57:39 +0800405 mouse.Move(delta_y=-90)
406 time.sleep(1)
407
Yu-Hsuan Hsu55b875e2020-02-12 10:49:17 +0800408 print('Move the cursor down right.')
Joseph Hwangd3784cb2016-03-06 21:57:39 +0800409 mouse.Move(delta_x=100, delta_y=90)
410 time.sleep(1)
411
Yu-Hsuan Hsu55b875e2020-02-12 10:49:17 +0800412 print('Scroll in one direction.')
Joseph Hwangd3784cb2016-03-06 21:57:39 +0800413 mouse.Scroll(-80)
414 time.sleep(1)
415
Yu-Hsuan Hsu55b875e2020-02-12 10:49:17 +0800416 print('Scroll in the opposite direction.')
Joseph Hwangd3784cb2016-03-06 21:57:39 +0800417 mouse.Scroll(100)
418 else:
Yu-Hsuan Hsu55b875e2020-02-12 10:49:17 +0800419 print('Something is wrong. Not able to connect to the remote address.')
Joseph Hwangd3784cb2016-03-06 21:57:39 +0800420 finally:
Joseph Hwang24ed76a2016-05-27 13:20:48 +0800421 if connected:
Yu-Hsuan Hsu55b875e2020-02-12 10:49:17 +0800422 print('Disconnecting...')
Joseph Hwang24ed76a2016-05-27 13:20:48 +0800423 try:
424 mouse.Disconnect()
Alexander Lentce006df2017-08-17 17:22:24 -0700425 # TODO(josephsih): Refactor test code to remove need for RN42 import
Joseph Hwang24ed76a2016-05-27 13:20:48 +0800426 except RN42Exception:
427 # RN-42 may have already disconnected.
428 pass
Joseph Hwangd3784cb2016-03-06 21:57:39 +0800429
Yu-Hsuan Hsu55b875e2020-02-12 10:49:17 +0800430 print('Closing the mouse...')
Joseph Hwangd3784cb2016-03-06 21:57:39 +0800431 mouse.Close()
432
433
434def _Parse():
435 """Parse the command line options."""
436 prog = sys.argv[0]
437 example_usage = ('Example:\n' +
438 ' python %s keyboard 00:11:22:33:44:55\n' % prog +
439 ' python %s mouse 00:11:22:33:44:55\n'% prog)
440 parser = argparse.ArgumentParser(
441 description='Emulate a HID device.\n' + example_usage,
442 formatter_class=argparse.RawTextHelpFormatter)
443 parser.add_argument('device',
444 choices=['keyboard', 'mouse'],
445 help='the device type to emulate')
446 parser.add_argument('remote_host_address',
447 help='the remote host address')
448 parser.add_argument('-c', '--chars_to_send',
449 default='echo hello world',
450 help='characters to send to the remote host')
451 args = parser.parse_args()
452
453 if len(args.remote_host_address.replace(':', '')) != 12:
Joseph Hwang2d426da2020-09-17 16:10:05 +0800454 print(('"%s" is not a valid bluetooth address.' % args.remote_host_address))
Joseph Hwangd3784cb2016-03-06 21:57:39 +0800455 exit(1)
456
Joseph Hwang2d426da2020-09-17 16:10:05 +0800457 print(('Emulate a %s and connect to remote host at %s' %
458 (args.device, args.remote_host_address)))
Joseph Hwangd3784cb2016-03-06 21:57:39 +0800459 return args
460
461
462def Demo():
463 """Make demonstrations about how to use the HID emulation classes."""
464 args = _Parse()
465 device = args.device.lower()
466 if device == 'keyboard':
467 DemoBluetoothHIDKeyboard(args.remote_host_address, args.chars_to_send)
468 elif device == 'mouse':
469 DemoBluetoothHIDMouse(args.remote_host_address)
470 else:
471 args.print_help()
472
473
Joseph Hwang5d0dc642016-01-19 10:21:12 +0800474if __name__ == '__main__':
Joseph Hwangd3784cb2016-03-06 21:57:39 +0800475 Demo()