Test production devices

Test locked devices and live apps through a Bluetooth mouse and keyboard and a camera.

Advanced, about 30 min

Optics sees the screen through a camera, finds the element with OCR, and taps it by moving a Bluetooth mouse, then parks the pointer in the corner.

Optics can test a device with no automation software on it at all. A BLE dongle (a small USB Bluetooth adapter) pairs with the device as an ordinary Bluetooth mouse and keyboard. A camera pointed at the screen shows Optics what's there. Optics finds things in the camera picture with OCR and image matching, then moves the pointer and taps.

When to use it#

Use this setup when the usual drivers can't reach the device:

  • USB debugging or developer mode isn't allowed, for example on locked company phones or retail devices.
  • The app blocks screenshots, as banking and streaming apps often do. The camera sees the screen anyway.
  • You monitor the live app. Synthetic monitoring runs the same journey on a real, unmodified device and app, the way a customer would.

If you can use Appium or a browser driver, do. They're faster, read the UI tree directly, and support every keyword.

What you need#

  • A USB serial BLE HID dongle. It plugs into your machine as a serial port, and pairs with the device as a Bluetooth mouse and keyboard.
  • A way to see the screen. Either a camera or capture card that your machine sees as a webcam, or a TCP frame server that sends a picture on request.
  • An OCR engine, such as EasyOCR, to read text in the camera picture.
  • Image templates (optional), cropped pictures of icons that have no text. See Find elements by text and image.
  • A stand that holds the camera square over the screen, so the picture doesn't shift between runs.

Set up#

  1. Install the engines#

    $ optics setup --install ble easyocr

    The ble engine adds serial-port support. The camera source and image matching need no extra install.

  2. Pair the dongle with the device#

    Plug the dongle into your machine. On the device, open Bluetooth settings and pair with the dongle like any keyboard. A pointer appears on screen once it's connected.

  3. Find the dongle's serial port#

    SystemCommandLooks like
    macOSls /dev/cu.usbmodem*/dev/cu.usbmodem1101
    Linuxls /dev/ttyACM*/dev/ttyACM0
    WindowsDevice Manager, under Ports (COM & LPT)COM3

    Instead of a path, you can give the dongle's USB product description, such as MZ-BLE-VMK-001-v1.2.1. Optics finds the matching port for you, which helps when the port number changes between reboots.

  4. Point the camera at the screen#

    Mount the camera straight on, with the screen upright and filling the picture edge to edge. Optics turns positions in the camera picture straight into pointer moves, so anything around the screen throws the taps off.

  5. Write config.yaml#

    config.yaml
    driver_sources:
      - ble:
          enabled: true
          capabilities:
            device_id: "Birch test phone"        # a label for the device
            port: /dev/ttyACM0                   # or the dongle's USB product description
            pixel_width: 1080                    # size of the screen in the camera picture
            pixel_height: 2336
            mickeys_width: 1080                  # pointer units to cross the screen
            mickeys_height: 2336
            x_invert: 1                          # -1 if the pointer moves the wrong way
            y_invert: 1
    elements_sources:
      - camera_screenshot:
          enabled: true
          capabilities:
            camera_index: 0                      # or url: "192.168.1.20:9000" for a frame server
    text_detection:
      - easyocr: {enabled: true}
    image_detection:
      - templatematch: {enabled: true}
  6. Calibrate, then run#

    Follow Calibrate the pointer below, then start the app on the device by hand and run:

    $ optics execute birch-production

    There's no preflight for this setup, so check the dongle and camera before you start a long run.

Settings#

ble driver#

NameDefaultWhat it does
device_idRequiredA name for the device.
portRequiredThe dongle's serial port (/dev/ttyACM0, /dev/cu.usbmodem1101, COM3), or its USB product description.
pixel_width, pixel_height0The size of the device screen in camera-picture pixels. Set both. Optics fails to start when they're 0.
mickeys_width, mickeys_height0How many pointer units it takes to cross the screen. Set both. Optics fails to start when they're 0.
x_invert, y_invert1Set to -1 to flip the direction the pointer moves on that axis.

camera_screenshot source#

NameDefaultWhat it does
camera_indexNoneThe webcam or capture card to use. 0 is the first one your system finds.
urlNoneA TCP frame server as host:port. Use instead of camera_index.
header_size8Frame server only. The length of the size header in bytes, 4 or 8.
byte_orderlittleFrame server only. The byte order of the size header, little or big.

Set either camera_index or url. With neither, Optics stops with "No camera_index or valid URL provided".

A frame server receives the line screenshot, then replies with the picture's size in a header_size-byte unsigned number, followed by the picture as JPEG or PNG.

Calibrate the pointer#

Optics resets the pointer to a corner before each tap, then moves it by the distance it calculated from the camera picture. Calibration makes that distance land in the right place.

  1. Set the pixel size. Set pixel_width and pixel_height to the size of the camera picture. Take one with Capture Screenshot and check its size.
  2. Start with equal units. Set mickeys_width and mickeys_height to the same numbers as the pixels.
  3. Tap a far corner. Run a one-step module, Press By Percentage,90,90, and watch where the pointer lands.
  4. Adjust. If the pointer stops short of the spot, raise the mickeys for that axis. If it overshoots, lower them. Repeat until the tap lands.
  5. Fix the direction. If the pointer moves left when it should move right, set x_invert: -1. Do the same with y_invert for up and down.

Calibrate again whenever you move the camera, change the device, or change its display size or orientation.

What works and what doesn't#

WorksHow
Press Element, Detect And PressWith text values (found by OCR) or image values. Optics moves the pointer there and taps.
Press By Coordinates, Press By PercentageTaps a spot in the camera picture.
Enter TextFinds the field by OCR or image, taps it, then types through the Bluetooth keyboard.
Enter Text DirectTypes into the field that has focus.
Assert Presence, Validate ScreenRead the camera picture with OCR or image matching.
Swipe, Swipe By Percentage, Swipe From ElementPress, drag and release with the pointer.
ScrollUp and down only.
Doesn't workInstead
Launch App, Launch Other AppStart the app by hand, or tap its icon on the home screen with Press Element.
XPath and UI-tree textThere's no UI tree. Use visible text, which OCR reads, or images.
Get Text, Get App Version, Execute ScriptNot available over Bluetooth.
Back, Volume, Power and other phone-only keysA Bluetooth keyboard has no such keys. Tap the app's own back button.
Named keys such as EnterOn 1.10.5, Press Keycode,Enter and <enter> type the letters of the key's name. Tap the on-screen key instead.
Error detection at the end of a runIt needs the page source, which a camera can't provide. Add Assert Presence checks for error messages you care about.

optics live, optics serve and optics mcp run Launch App when they start, so that first step fails with this setup. Start the app by hand before you begin.

Here is a sign-in journey written for a production phone. Every value is text that OCR can read on screen:

modules/modules.csv
module_name,module_step,param_1,param_2
Open Birch,Press Element,Birch
Open Birch,Assert Presence,Sign in,20
Sign in,Enter Text,Email,[email protected]
Sign in,Enter Text,Password,@:trailrunner
Sign in,Press Element,Sign in
Sign in,Assert Presence,Trail Runner 2,20

The first step taps the Birch icon on the home screen, so the run starts from there.

Known gaps#

  • Camera correction isn't applied. deskew_corners, rotation, out_width and out_height under camera_screenshot have no effect in 1.10.5. Frame the camera straight on, with the screen upright, instead.
  • The doctor reports a failure. optics doctor expects an element source whose name starts with ble_, and none exists. It shows ❌ for config: ble element source. Ignore that row, and don't use optics doctor --check on this project in CI.

If it isn't working#

  • "Could not resolve serial port for product description". No connected port has that description. Check the dongle is plugged in, or use the port path instead.
  • Optics stops at start with a division error. pixel_width, pixel_height, mickeys_width or mickeys_height is 0 or missing. Set all four.
  • "No camera_index or valid URL provided". Add camera_index or url under the camera_screenshot capabilities.
  • Taps land in the wrong place. Recalibrate. Check that the screen fills the camera picture and that nothing moved.
  • Text isn't found. Improve the picture: even light, no glare, the camera in focus. Use an area of interest to search a smaller part of the screen.
  • The pointer doesn't move. Check the device is still paired with the dongle and that its pointer shows on screen.

See also#