blob: a8f73ae2fbc8526318b0f213fb2a12af45802213 [file] [log] [blame]
Neeraj Poojary59f8b152019-02-01 14:15:57 -08001# Copyright 2019 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
Archie Pusaka3c9fd0c2021-04-13 15:26:46 +08005"""This module provides an abstraction of the Nordic nRF52 BLE kit."""
Neeraj Poojary59f8b152019-02-01 14:15:57 -08006
7from __future__ import print_function
8
Archie Pusaka3c9fd0c2021-04-13 15:26:46 +08009# TODO: to port chromite.lib.cros_logging to replace legacy logging
10import logging # pylint: disable=cros-logging-import
Neeraj Poojary59f8b152019-02-01 14:15:57 -080011import time
12
Yu-Hsuan Hsu04cff0e2020-02-13 11:55:10 +080013from .bluetooth_peripheral_kit import PeripheralKit
14from .bluetooth_peripheral_kit import PeripheralKitException
Neeraj Poojary59f8b152019-02-01 14:15:57 -080015
16
17class nRF52Exception(PeripheralKitException):
18 """A dummy exception class for nRF52 class."""
Neeraj Poojary59f8b152019-02-01 14:15:57 -080019
20
21class nRF52(PeripheralKit):
Archie Pusaka3c9fd0c2021-04-13 15:26:46 +080022 """This is an abstraction of Nordic's nRF52 Dongle
23
24 It is used to emulate BLE mouse and keyboard functionality.
Neeraj Poojary59f8b152019-02-01 14:15:57 -080025
26 SDK: https://www.nordicsemi.com/Software-and-Tools/Software/nRF5-SDK
27
28 See autotest-private/nRF52/ble_app_hids/README for information about
29 using the SDK to compile the application.
30 """
31
32 # Serial port settings (override)
33 BAUDRATE = 115200
34 DRIVER = 'cdc_acm'
35 # Driver name in udev is 'cdc_acm', but builtin module is 'cdc-acm.ko'
36 # So we need to look for cdc_acm when searching by driver,
37 # but looking in builtins requires searching by 'cdc-acm'.
38 DRIVER_MODULE = 'cdc-acm'
39 BAUDRATE = 115200
Neeraj Poojary7a93d5a2019-07-01 13:56:10 -070040 USB_VID = '1366'
41 USB_PID = '1015'
Neeraj Poojary59f8b152019-02-01 14:15:57 -080042
43 # A newline can just be a '\n' to denote the end of a command
44 NEWLINE = '\n'
45 CMD_FS = ' ' # Command field separator
46
47 # Supported device types
48 MOUSE = 'MOUSE'
49 KEYBOARD = 'KEYBOARD'
Yoni Shavit75d7cf12019-03-27 13:35:05 -070050 KNOWN_DEVICE_SET = None
Neeraj Poojary59f8b152019-02-01 14:15:57 -080051
52 RESET_SLEEP_SECS = 1
53
54 # Mouse button constants
55 MOUSE_BUTTON_LEFT_BIT = 1
56 MOUSE_BUTTON_RIGHT_BIT = 2
57
58 # Specific Commands
59 # Reboot the nRF52
Archie Pusaka3c9fd0c2021-04-13 15:26:46 +080060 CMD_REBOOT = 'RBT'
Neeraj Poojary59f8b152019-02-01 14:15:57 -080061 # Reset the nRF52 and erase all previous bonds
Archie Pusaka3c9fd0c2021-04-13 15:26:46 +080062 CMD_FACTORY_RESET = 'FRST'
Neeraj Poojary59f8b152019-02-01 14:15:57 -080063 # Return the name that is sent in advertisement packets
Archie Pusaka3c9fd0c2021-04-13 15:26:46 +080064 CMD_GET_ADVERTISED_NAME = 'GN'
Neeraj Poojary59f8b152019-02-01 14:15:57 -080065 # Return the nRF52 firmware version
Archie Pusaka3c9fd0c2021-04-13 15:26:46 +080066 CMD_GET_FIRMWARE_VERSION = 'GV'
Neeraj Poojary59f8b152019-02-01 14:15:57 -080067 # Return the Bluetooth address of the nRF52
Archie Pusaka3c9fd0c2021-04-13 15:26:46 +080068 CMD_GET_NRF52_MAC = 'GM'
Neeraj Poojary59f8b152019-02-01 14:15:57 -080069 # Return the address of the device connected (if there exists a connection)
Archie Pusaka3c9fd0c2021-04-13 15:26:46 +080070 CMD_GET_REMOTE_CONNECTION_MAC = 'GC'
Neeraj Poojary59f8b152019-02-01 14:15:57 -080071 # Return the status of the nRF52's connection with a central device
Archie Pusaka3c9fd0c2021-04-13 15:26:46 +080072 CMD_GET_CONNECTION_STATUS = 'GS'
Neeraj Poojary59f8b152019-02-01 14:15:57 -080073
74 # Return the type of device the HID service is set
Archie Pusaka3c9fd0c2021-04-13 15:26:46 +080075 CMD_GET_DEVICE_TYPE = 'GD'
Neeraj Poojary59f8b152019-02-01 14:15:57 -080076 # Set the nRF52 HID service to mouse
Archie Pusaka3c9fd0c2021-04-13 15:26:46 +080077 CMD_SET_MOUSE = 'SM'
Neeraj Poojary59f8b152019-02-01 14:15:57 -080078 # Set the nRF52 HID service to keyboard
Archie Pusaka3c9fd0c2021-04-13 15:26:46 +080079 CMD_SET_KEYBOARD = 'SK'
Neeraj Poojary59f8b152019-02-01 14:15:57 -080080 # Start HID service emulation
Archie Pusaka3c9fd0c2021-04-13 15:26:46 +080081 CMD_START_HID_EM = 'START'
Neeraj Poojary59f8b152019-02-01 14:15:57 -080082 # Start HID service emulation
Archie Pusaka3c9fd0c2021-04-13 15:26:46 +080083 CMD_STOP_HID_EM = 'STOP'
Neeraj Poojary59f8b152019-02-01 14:15:57 -080084 # Start advertising with the current settings (HID type)
Archie Pusaka3c9fd0c2021-04-13 15:26:46 +080085 CMD_START_ADVERTISING = 'ADV'
Neeraj Poojary59f8b152019-02-01 14:15:57 -080086
87 # Press (or clear) one or more buttons (left/right)
Archie Pusaka3c9fd0c2021-04-13 15:26:46 +080088 CMD_MOUSE_BUTTON = 'B'
Neeraj Poojary59f8b152019-02-01 14:15:57 -080089 # Click the left and/or right button of the mouse
Archie Pusaka3c9fd0c2021-04-13 15:26:46 +080090 CMD_MOUSE_CLICK = 'C'
Neeraj Poojary59f8b152019-02-01 14:15:57 -080091 # Move the mouse along x and/or y axis
Archie Pusaka3c9fd0c2021-04-13 15:26:46 +080092 CMD_MOUSE_MOVE = 'M'
Neeraj Poojary59f8b152019-02-01 14:15:57 -080093 # Scrolling the mouse wheel up/down
Archie Pusaka3c9fd0c2021-04-13 15:26:46 +080094 CMD_MOUSE_SCROLL = 'S'
Neeraj Poojary59f8b152019-02-01 14:15:57 -080095
96 def GetCapabilities(self):
97 """What can this kit do/not do that tests need to adjust for?
98
99 Returns:
100 A dictionary from PeripheralKit.CAP_* strings to an appropriate value.
101 See above (CAP_*) for details.
102 """
103 return {PeripheralKit.CAP_TRANSPORTS: [PeripheralKit.TRANSPORT_LE],
104 PeripheralKit.CAP_HAS_PIN: False,
105 PeripheralKit.CAP_INIT_CONNECT: False}
106
107 def EnterCommandMode(self):
108 """Make the kit enter command mode.
109
110 The application on the nRF52 Dongle is always in command mode, so this
111 method will just create a serial connection if necessary
112
113 Returns:
114 True if the kit successfully entered command mode.
115
116 Raises:
117 nRF52Exception if there is an error in creating the serial connection
118 """
119 if self._serial is None:
120 self.CreateSerialDevice()
121 if not self._command_mode:
122 self._command_mode = True
123 return True
124
125 def LeaveCommandMode(self, force=False):
126 """Make the kit leave command mode.
127
128 As above, the nRF52 application is always in command mode.
129
130 Args:
131 force: True if we want to ignore potential errors and leave command mode
132 regardless of those errors
133
134 Returns:
135 True if the kit successfully left command mode.
136 """
137 if self._command_mode or force:
138 self._command_mode = False
139 return True
140
141 def Reboot(self):
142 """Reboot the nRF52 Dongle.
143
144 Does not erase the bond information.
145
146 Returns:
147 True if the kit rebooted successfully.
148 """
149 self.SerialSendReceive(self.CMD_REBOOT,
150 msg='rebooting nRF52')
151 time.sleep(self.RESET_SLEEP_SECS)
152 return True
153
154 def FactoryReset(self):
155 """Factory reset the nRF52 Dongle.
156
157 Erase the bond information and reboot.
158
159 Returns:
160 True if the kit is reset successfully.
161 """
162 self.SerialSendReceive(self.CMD_FACTORY_RESET,
163 msg='factory reset nRF52')
164 time.sleep(self.RESET_SLEEP_SECS)
165 return True
166
167 def GetAdvertisedName(self):
168 """Get the name advertised by the nRF52.
169
170 Returns:
171 The device name that the application uses in advertising
172 """
173 return self.SerialSendReceive(self.CMD_GET_ADVERTISED_NAME,
174 msg='getting advertised name')
175
176 def GetFirmwareVersion(self):
177 """Get the firmware version of the kit.
178
179 This is useful for checking what features are supported if we want to
180 support muliple versions of some kit.
181
182 For nRF52, returns the Link Layer Version (8 corresponds to BT 4.2),
183 Nordic Company ID (89), and Firmware ID (135).
184
185 Returns:
186 The firmware version of the kit.
187 """
188 return self.SerialSendReceive(self.CMD_GET_FIRMWARE_VERSION,
189 msg='getting firmware version')
190
191 def GetOperationMode(self):
192 """Get the operation mode.
193
Archie Pusaka8b872a72021-04-13 14:59:50 +0800194 This is CENTRAL or PERIPHERAL for both BR/EDR and LE.
Neeraj Poojary59f8b152019-02-01 14:15:57 -0800195 Not all kits may support all modes.
196
197 nRF52 only supports peripheral role
198
199 Returns:
200 The operation mode of the kit.
201 """
202 logging.debug('GetOperationMode is a NOP on nRF52')
Archie Pusaka8b872a72021-04-13 14:59:50 +0800203 return 'PERIPHERAL'
Neeraj Poojary59f8b152019-02-01 14:15:57 -0800204
Archie Pusaka8b872a72021-04-13 14:59:50 +0800205 def SetCentralMode(self):
206 """Set the kit to central mode.
Neeraj Poojary59f8b152019-02-01 14:15:57 -0800207
208 nRF52 application only acts as a peripheral
209
210 Returns:
Archie Pusaka8b872a72021-04-13 14:59:50 +0800211 True if central mode was set successfully.
Neeraj Poojary59f8b152019-02-01 14:15:57 -0800212
213 Raises:
Archie Pusaka8b872a72021-04-13 14:59:50 +0800214 A kit-specific exception if central mode is unsupported.
Neeraj Poojary59f8b152019-02-01 14:15:57 -0800215 """
Archie Pusaka8b872a72021-04-13 14:59:50 +0800216 error_msg = 'Failed to set central mode'
Neeraj Poojary59f8b152019-02-01 14:15:57 -0800217 logging.error(error_msg)
218 raise nRF52Exception(error_msg)
219
Archie Pusaka8b872a72021-04-13 14:59:50 +0800220 def SetPeripheralMode(self):
221 """Set the kit to peripheral mode.
Neeraj Poojary59f8b152019-02-01 14:15:57 -0800222
223 Silently succeeds, because the nRF52 application is always a peripheral
224
225 Returns:
Archie Pusaka8b872a72021-04-13 14:59:50 +0800226 True if peripheral mode was set successfully.
Neeraj Poojary59f8b152019-02-01 14:15:57 -0800227
228 Raises:
Archie Pusaka8b872a72021-04-13 14:59:50 +0800229 A kit-specific exception if peripheral mode is unsupported.
Neeraj Poojary59f8b152019-02-01 14:15:57 -0800230 """
Archie Pusaka8b872a72021-04-13 14:59:50 +0800231 logging.debug('SetPeripheralMode is a NOP on nRF52')
Neeraj Poojary59f8b152019-02-01 14:15:57 -0800232 return True
233
234 def GetAuthenticationMode(self):
235 """Get the authentication mode.
236
237 This specifies how the device will authenticate with the DUT, for example,
238 a PIN code may be used.
239
240 Not supported on nRF52 application.
241
242 Returns:
243 None as the nRF52 does not support an Authentication mode.
244 """
245 logging.debug('GetAuthenticationMode is a NOP on nRF52')
Archie Pusaka3c9fd0c2021-04-13 15:26:46 +0800246
Neeraj Poojary59f8b152019-02-01 14:15:57 -0800247
248 def SetAuthenticationMode(self, mode):
249 """Set the authentication mode to the specified mode.
250
251 If mode is PIN_CODE_MODE, implementations must ensure the default PIN
252 is set by calling _SetDefaultPinCode() as appropriate.
253
254 Not supported on nRF52 application.
255
256 Args:
257 mode: the desired authentication mode (specified in PeripheralKit)
258
259 Returns:
260 True if the mode was set successfully,
261
262 Raises:
263 A kit-specific exception if given mode is not supported.
264 """
265 error_msg = 'nRF52 does not support authentication mode'
266 logging.error(error_msg)
267 raise nRF52Exception(error_msg)
268
269 def GetPinCode(self):
270 """Get the pin code.
271
272 Returns:
273 A string representing the pin code,
274 None if there is no pin code stored.
275 """
276 warn_msg = 'nRF52 does not support PIN code mode, no PIN exists'
Archie Pusaka3c9fd0c2021-04-13 15:26:46 +0800277 logging.warning(warn_msg)
278
Neeraj Poojary59f8b152019-02-01 14:15:57 -0800279
280 def SetPinCode(self, pin):
281 """Set the pin code.
282
283 Not support on nRF52 application.
284
285 Returns:
286 True if the pin code is set successfully,
287
288 Raises:
289 A kit-specifc exception if the pin code is invalid.
290 """
291 error_msg = 'nRF52 does not support PIN code mode'
292 logging.error(error_msg)
293 raise nRF52Exception(error_msg)
294
295 def GetServiceProfile(self):
296 """Get the service profile.
297
298 Unrelated to HID for the nRF52 application, so ignore for now
299
300 Returns:
301 The service profile currently in use (as per constant in PeripheralKit)
302 """
303 logging.debug('GetServiceProfile is a NOP on nRF52')
Archie Pusaka3c9fd0c2021-04-13 15:26:46 +0800304 return 'HID'
Neeraj Poojary59f8b152019-02-01 14:15:57 -0800305
306 def SetServiceProfileSPP(self):
307 """Set SPP as the service profile.
308
309 Unrelated to HID for the nRF52 application, so ignore for now
310
311 Returns:
312 True if the service profile was set to SPP successfully.
313
314 Raises:
315 A kit-specifc exception if unsuppported.
316 """
317 error_msg = 'Failed to set SPP service profile'
318 logging.error(error_msg)
319 raise nRF52Exception(error_msg)
320
321 def SetServiceProfileHID(self):
322 """Set HID as the service profile.
323
324 nRF52 application only does HID at the moment. Silently succeeds
325
326 Returns:
327 True if the service profile was set to HID successfully.
328 """
329 logging.debug('SetServiceProfileHID is a NOP on nRF52')
330 return True
331
332 def GetLocalBluetoothAddress(self):
333 """Get the address advertised by the nRF52, which is the MAC address.
334
335 Address is returned as XX:XX:XX:XX:XX:XX
336
337 Returns:
338 The address of the nRF52 if successful or None if it fails
339 """
340 address = self.SerialSendReceive(self.CMD_GET_NRF52_MAC,
341 msg='getting local MAC address')
342 return address
343
344 def GetRemoteConnectedBluetoothAddress(self):
345 """Get the address of the device that is connected to the nRF52.
346
347 Address is returned as XX:XX:XX:XX:XX:XX
348 If not connected, nRF52 will return 00:00:00:00:00:00
349
350 Returns:
351 The address of the connected device or a null address if successful.
352 None if the serial receiving fails
353 """
354 address = self.SerialSendReceive(self.CMD_GET_REMOTE_CONNECTION_MAC,
355 msg='getting remote MAC address')
356 if len(address) == 17:
357 return address
358 else:
Archie Pusaka3c9fd0c2021-04-13 15:26:46 +0800359 logging.error('remote connection address is invalid: %s', address)
Neeraj Poojary59f8b152019-02-01 14:15:57 -0800360 return None
361
362 def GetConnectionStatus(self):
363 """Get whether the nRF52 is connected to another device.
364
365 nRF52 returns a string 'INVALID' or 'CONNECTED'
366
367 Returns:
368 True if the nRF52 is connected to another device
369 """
370 result = self.SerialSendReceive(self.CMD_GET_CONNECTION_STATUS,
371 msg = 'getting connection status')
372 return result == 'CONNECTED'
373
374 def EnableConnectionStatusMessage(self):
375 """Enable the connection status message.
376
377 On some kits, this is required to use connection-related methods.
378
379 Not supported by the nRF52 application for now. This could be
380 changed so that Connection Status Messages are sent by nRF52.
381
382 Returns:
383 True if enabling the connection status message successfully.
384 """
385 logging.debug('EnableConnectionStatusMessage is a NOP on nRF52')
386 return True
387
388 def DisableConnectionStatusMessage(self):
389 """Disable the connection status message.
390
391 Not supported by the nRF52 application for now. This could be
392 changed so that Connection Status Messages are sent by nRF52.
393
394 Returns:
395 True if disabling the connection status message successfully.
396 """
397 logging.debug('DisableConnectionStatusMessage is a NOP on nRF52')
398 return True
399
Joseph Hwang14140862020-01-10 16:25:02 +0800400 def GetDeviceType(self):
401 """Get the device type.
Neeraj Poojary59f8b152019-02-01 14:15:57 -0800402
403 Returns:
Joseph Hwang14140862020-01-10 16:25:02 +0800404 A string representing the device type
Neeraj Poojary59f8b152019-02-01 14:15:57 -0800405 """
406 return self.SerialSendReceive(self.CMD_GET_DEVICE_TYPE,
Joseph Hwang14140862020-01-10 16:25:02 +0800407 msg='getting the device type')
Neeraj Poojary59f8b152019-02-01 14:15:57 -0800408
409 def SetHIDType(self, device_type):
410 """Set HID type to the specified device type.
411
412 Args:
413 device_type: the HID type to emulate, from PeripheralKit
414 (MOUSE, KEYBOARD)
415
416 Returns:
417 True if successful
418
419 Raises:
420 A kit-specific exception if that device type is not supported.
421 """
422 if device_type == self.MOUSE:
423 result = self.SerialSendReceive(self.CMD_SET_MOUSE,
424 msg='setting mouse as HID type')
425 print(result)
426 elif device_type == self.KEYBOARD:
427 self.SerialSendReceive(self.CMD_SET_KEYBOARD,
428 msg='setting keyboard as HID type')
429 else:
Archie Pusaka3c9fd0c2021-04-13 15:26:46 +0800430 msg = 'Failed to set HID type, not supported: %s' % device_type
Neeraj Poojary59f8b152019-02-01 14:15:57 -0800431 logging.error(msg)
432 raise nRF52Exception(msg)
433 return True
434
435 def GetClassOfService(self):
436 """Get the class of service, if supported.
437
438 Not supported on nRF52
439
440 Returns:
441 None, the only reasonable value for BLE-only devices
442 """
443 logging.debug('GetClassOfService is a NOP on nRF52')
Archie Pusaka3c9fd0c2021-04-13 15:26:46 +0800444
Neeraj Poojary59f8b152019-02-01 14:15:57 -0800445
446 def SetClassOfService(self, class_of_service):
447 """Set the class of service, if supported.
448
449 The class of service is a number usually assigned by the Bluetooth SIG.
450 Usually supported only on BR/EDR kits.
451
452 Not supported on nRF52, but fake it
453
454 Args:
455 class_of_service: A decimal integer representing the class of service.
456
457 Returns:
458 True as this action is not supported.
459 """
460 logging.debug('SetClassOfService is a NOP on nRF52')
461 return True
462
463 def GetClassOfDevice(self):
464 """Get the class of device, if supported.
465
466 The kit uses a hexadeciaml string to represent the class of device.
467 It is converted to a decimal number as the return value.
468 The class of device is a number usually assigned by the Bluetooth SIG.
469 Usually supported only on BR/EDR kits.
470
471 Not supported on nRF52, so None
472
473 Returns:
474 None, the only reasonable value for BLE-only devices.
475 """
476 logging.debug('GetClassOfDevice is a NOP on nRF52')
Archie Pusaka3c9fd0c2021-04-13 15:26:46 +0800477
Neeraj Poojary59f8b152019-02-01 14:15:57 -0800478
479 def SetClassOfDevice(self, device_type):
480 """Set the class of device, if supported.
481
482 The class of device is a number usually assigned by the Bluetooth SIG.
483 Usually supported only on BR/EDR kits.
484
485 Not supported on nRF52, but fake it.
486
487 Args:
488 device_type: A decimal integer representing the class of device.
489
490 Returns:
491 True as this action is not supported.
492 """
493 logging.debug('SetClassOfDevice is a NOP on nRF52')
494 return True
495
496 def SetRemoteAddress(self, remote_address):
497 """Set the remote Bluetooth address.
498
499 (Usually this will be the device under test that we want to connect with,
500 where the kit starts the connection.)
501
502 Not supported on nRF52 HID application.
503
504 Args:
505 remote_address: the remote Bluetooth MAC address, which must be given as
506 12 hex digits with colons between each pair.
507 For reference: '00:29:95:1A:D4:6F'
508
509 Returns:
510 True if the remote address was set successfully.
511
512 Raises:
513 PeripheralKitException if the given address was malformed.
514 """
515 error_msg = 'Failed to set remote address'
516 logging.error(error_msg)
517 raise nRF52Exception(error_msg)
518
519 def Connect(self):
520 """Connect to the stored remote bluetooth address.
521
522 In the case of a timeout (or a failure causing an exception), the caller
523 is responsible for retrying when appropriate.
524
525 Not supported on nRF52 HID application.
526
527 Returns:
528 True if connecting to the stored remote address succeeded, or
529 False if a timeout occurs.
530 """
531 error_msg = 'Failed to connect to remote device'
532 logging.error(error_msg)
533 raise nRF52Exception(error_msg)
534
535 def Disconnect(self):
536 """Disconnect from the remote device.
537
538 Specifically, this causes the peripheral emulation kit to disconnect from
539 the remote connected device, usually the DUT.
540
541 Returns:
542 True if disconnecting from the remote device succeeded.
543 """
544 self.SerialSendReceive(self.CMD_DISCONNECT,
545 msg='disconnect')
546 return True
547
Archie Pusaka3c9fd0c2021-04-13 15:26:46 +0800548 def Discover(self, remote_address):
549 """Discover the remote address.
550
551 Not supported on nRF52 HID application.
552
553 Args:
554 remote_address: the remote Bluetooth address, which must be given as 12
555 hex digits with colons between each pair.
556 For reference: '00:29:95:1A:D4:6F'
557
558 Returns:
559 True if discovering the remote address succeeded
560 """
561 error_msg = 'nRF52 does not support discovery'
562 logging.error(error_msg)
563 raise nRF52Exception(error_msg)
564
Neeraj Poojary59f8b152019-02-01 14:15:57 -0800565 def StartAdvertising(self):
566 """Command the nRF52 to begin advertising with its current settings.
567
568 Returns:
569 True if successful.
570 """
571 self.SerialSendReceive(self.CMD_START_ADVERTISING,
572 msg='start advertising')
573 return True
574
575 def MouseMove(self, delta_x, delta_y):
576 """Move the mouse (delta_x, delta_y) steps.
577
578 Buttons currently pressed will stay pressed during this operation.
579 This move is relative to the current position by the HID standard.
580 Valid step values must be in the range [-127,127].
581
582 Args:
583 delta_x: The number of steps to move horizontally.
584 Negative values move left, positive values move right.
585 delta_y: The number of steps to move vertically.
586 Negative values move up, positive values move down.
587
588 Returns:
589 True if successful.
590 """
591 command = self.CMD_MOUSE_MOVE + self.CMD_FS
592 command += str(delta_x) + self.CMD_FS + str(delta_y)
Archie Pusaka3c9fd0c2021-04-13 15:26:46 +0800593 message = 'moving BLE mouse ' + str(delta_x) + ' ' + str(delta_y)
594 self.SerialSendReceive(command, msg=message)
Neeraj Poojary59f8b152019-02-01 14:15:57 -0800595 return True
596
597 def MouseScroll(self, steps):
598 """Scroll the mouse wheel steps number of steps.
599
600 Buttons currently pressed will stay pressed during this operation.
601 Valid step values must be in the range [-127,127].
602
603 Args:
604 steps: The number of steps to scroll the wheel.
605 With traditional scrolling:
606 Negative values scroll down, positive values scroll up.
607 With reversed (formerly "Australian") scrolling this is reversed.
608
609 Returns:
610 True if successful.
611 """
612 command = self.CMD_MOUSE_SCROLL + self.CMD_FS
613 command += self.CMD_FS
614 command += str(steps) + self.CMD_FS
615 message = 'scrolling BLE mouse'
Archie Pusaka3c9fd0c2021-04-13 15:26:46 +0800616 self.SerialSendReceive(command, msg=message)
Neeraj Poojary59f8b152019-02-01 14:15:57 -0800617 return True
618
619 def MouseHorizontalScroll(self, steps):
620 """Horizontally scroll the mouse wheel steps number of steps.
621
622 Buttons currently pressed will stay pressed during this operation.
623 Valid step values must be in the range [-127,127].
624
625 There is no nRF52 limitation for implementation. If we can program
626 the correct HID event report to emulate horizontal scrolling, this
627 can be supported.
Archie Pusaka3c9fd0c2021-04-13 15:26:46 +0800628
Neeraj Poojary59f8b152019-02-01 14:15:57 -0800629 **** Not implemented ****
Archie Pusaka3c9fd0c2021-04-13 15:26:46 +0800630
Neeraj Poojary59f8b152019-02-01 14:15:57 -0800631 Args:
632 steps: The number of steps to scroll the wheel.
633 With traditional scrolling:
634 Negative values scroll left, positive values scroll right.
635 With reversed (formerly "Australian") scrolling this is reversed.
636
637 Returns:
638 True if successful.
639 """
Archie Pusaka3c9fd0c2021-04-13 15:26:46 +0800640
641 del steps # to silent linter warning
Neeraj Poojary59f8b152019-02-01 14:15:57 -0800642 return True
643
644 def _MouseButtonCodes(self):
645 """Gives the letter codes for whatever buttons are pressed.
646
647 Returns:
648 A int w/ bits representing pressed buttons.
649 """
650 currently_pressed = 0
651 for button in self._buttons_pressed:
652 if button == PeripheralKit.MOUSE_BUTTON_LEFT:
653 currently_pressed += self.MOUSE_BUTTON_LEFT_BIT
654 elif button == PeripheralKit.MOUSE_BUTTON_RIGHT:
655 currently_pressed += self.MOUSE_BUTTON_RIGHT_BIT
656 else:
Archie Pusaka3c9fd0c2021-04-13 15:26:46 +0800657 error = 'Unknown mouse button in state: %s' % button
Neeraj Poojary59f8b152019-02-01 14:15:57 -0800658 logging.error(error)
659 raise nRF52Exception(error)
660 return currently_pressed
661
662 def MousePressButtons(self, buttons):
663 """Press the specified mouse buttons.
664
665 The kit will continue to press these buttons until otherwise instructed, or
666 until its state has been reset.
667
668 Args:
669 buttons: A set of buttons, as PeripheralKit MOUSE_BUTTON_* values, that
670 will be pressed (and held down).
671
672 Returns:
673 True if successful.
674 """
675 self._MouseButtonStateUnion(buttons)
676 button_codes = self._MouseButtonCodes()
677 command = self.CMD_MOUSE_BUTTON + self.CMD_FS
678 command += str(button_codes)
679 message = 'pressing BLE mouse buttons'
Archie Pusaka3c9fd0c2021-04-13 15:26:46 +0800680 self.SerialSendReceive(command, msg=message)
Neeraj Poojary59f8b152019-02-01 14:15:57 -0800681 return True
682
683 def MouseReleaseAllButtons(self):
684 """Release all mouse buttons.
685
686 Returns:
687 True if successful.
688 """
689 self._MouseButtonStateClear()
690 command = self.CMD_MOUSE_BUTTON + self.CMD_FS
691 command += '0'
692 message = 'releasing all BLE HOG mouse buttons'
Archie Pusaka3c9fd0c2021-04-13 15:26:46 +0800693 self.SerialSendReceive(command, msg=message)
Neeraj Poojary59f8b152019-02-01 14:15:57 -0800694 return True
695
Archie Pusaka3c9fd0c2021-04-13 15:26:46 +0800696 def SetDiscoverable(self, discoverable):
697 """Sets the discoverability of the device.
698
699 Not supported on nRF52 application.
700
701 Args:
702 discoverable: Whether device is discoverable/advertising.
703 """
704 error_msg = 'nRF52 does not support discoverable'
705 logging.error(error_msg)
706 raise nRF52Exception(error_msg)
707
Neeraj Poojary59f8b152019-02-01 14:15:57 -0800708 def Reset(self):
Archie Pusaka3c9fd0c2021-04-13 15:26:46 +0800709 self.SerialSendReceive(nRF52.CMD_REBOOT, msg='reset nRF52')
Neeraj Poojary59f8b152019-02-01 14:15:57 -0800710 return True
711
712 def SetModeMouse(self):
713 self.EnterCommandMode()
Archie Pusaka3c9fd0c2021-04-13 15:26:46 +0800714 self.SerialSendReceive(nRF52.CMD_SET_MOUSE, msg='set nRF52 mouse')
Neeraj Poojary59f8b152019-02-01 14:15:57 -0800715 return True
716
717 def GetKitInfo(self, connect_separately=False, test_reset=False):
718 """A simple demo of getting kit information."""
719 if connect_separately:
720 print('create serial device: %s' % self.CreateSerialDevice())
721 if test_reset:
722 print('factory reset: %s' % self.FactoryReset())
723 self.EnterCommandMode()
724 print('advertised name: %s' % self.GetAdvertisedName())
725 print('firmware version: %s' % self.GetFirmwareVersion())
726 print('local bluetooth address: %s' % self.GetLocalBluetoothAddress())
727 print('connection status: %s' % self.GetConnectionStatus())
728 # The class of service/device is None for LE kits (it is BR/EDR-only)
729
730
731if __name__ == '__main__':
732 kit_instance = nRF52()
733 kit_instance.GetKitInfo()