API Reference
Complete documentation for every function in the MAS Python SDK.
Namespace Map
| Device | get_screen_size, get_device_info, get_host_machine_id |
| Interaction | click, swipe, input_text, key_press |
| Vision | take_screenshot, find_object, find_objects, find_any_object, wait_for_object, read_text |
| Image Library | images() |
| Apps | open_app, close_app, get_current_app, get_app_state, is_app_focused |
| Storage | save, retrieve, retrieve_all, clear, get_current_device_port |
| Utilities | get_clipboard |
| Runtime UI | mas.ui.set_text, set_progress, add_data_point, append_text, set_table_data, on_click, batch, ... |
Types
All types are importable from mas (e.g. from mas import Region, KeyCode).
Region
dataclassA rectangular region on screen defined by corner coordinates.
region = Region(x1=100, y1=200, x2=900, y2=260)
| Field | Type | Description |
|---|---|---|
| x1 | int | Left edge x-coordinate (pixels from left) |
| y1 | int | Top edge y-coordinate (pixels from top) |
| x2 | int | Right edge x-coordinate (pixels from left) |
| y2 | int | Bottom edge y-coordinate (pixels from top) |
Coordinates
type aliasAn (x, y) tuple. swipe takes these; ObjectMatch.center returns one.
Screenshot
dataclassResult of a screenshot capture operation.
| Field | Type | Description |
|---|---|---|
| base64 | str | Base64-encoded PNG image data |
| width | int | Screenshot width in pixels |
| height | int | Screenshot height in pixels |
| timestamp | str | Capture timestamp in ISO 8601 format |
ObjectMatch
dataclassResult of a template matching operation. x and y are the center of the match.
| Field | Type | Description |
|---|---|---|
| x | int | Center x coordinate |
| y | int | Center y coordinate |
| center | tuple[int, int] | Property returning (x, y) — use as mas.click(*match.center) |
| matched_template_id | int | Image ID that matched (relevant after find_any_object) |
TextRecognitionResult
dataclassResult of OCR text recognition.
| Field | Type | Description |
|---|---|---|
| text | str | Extracted text |
| confidence | float | Recognition confidence (0.0 to 1.0) |
| region | Region | Region where text was found |
ScreenSize
dataclasswidth: int
height: int
DeviceInfo
dataclassid, name: str
type: "android" | "desktop"
screen_width, screen_height: int
connected: bool
KeyCode
enumAndroid hardware key codes for key_press.
BACK HOME MENU APP_SWITCH ENTER DELETE SPACE TAB POWER CAMERA VOLUME_UP VOLUME_DOWN BRIGHTNESS_UP BRIGHTNESS_DOWN DPAD_UP DPAD_DOWN DPAD_LEFT DPAD_RIGHT DPAD_CENTER MEDIA_PLAY MEDIA_PAUSE MEDIA_NEXT MEDIA_PREVIOUS + NUM_0–NUM_9 for the numeric keypad.
Modifier
enumSHIFT
CTRL
ALT
META
ColorConversion
enumNONE
BGR_TO_GRAY
RGB_TO_GRAY
+ HSV / RGB variants for special cases.
SearchStrategy
literal"first_match"
"best_match"
"priority_order"
ImageMap / ImageRef
returned bymas.images() An ImageMap is a read-only object with one ImageRef per declared image, reached by dot access. Any vision call accepts an ImageRef directly. See the Image Library section.
| Field | Type | Description |
|---|---|---|
| id | int | The Image Library ID |
| name | str | The attribute name from the mas.images() mapping |
UIEvent
returned bymas.ui.wait_for_event An event fired by a runtime dashboard widget the user interacted with.
| Field | Type | Description |
|---|---|---|
| widget_id | str | The widget's name from the .uibrt |
| event_type | str | "click" or "change" |
| value | Any | Current widget value (for input changes) |
Device Control
mas.get_screen_size() ScreenSize
Get the current device screen dimensions in pixels. Use it to compute coordinates so the macro survives a resolution change.
mas.click(size.width // 2, size.height // 2) # tap center
mas.get_device_info() DeviceInfo
Get detailed metadata about the connected device.
print(f"{info.name}{info.screen_width}x{info.screen_height}")
mas.get_host_machine_id() str
A stable UUID for the PC the macro runs on — the same across restarts, shared by every macro on that machine. It is the default machine_id for storage, so you rarely call it directly. Raises RPCError rather than returning an empty string.
User Interaction
mas.click(x, y, delay_ms=1000) None
Tap at pixel (x, y). delay_ms is a pause after the tap — useful for letting a screen settle. Default 1000 ms; drop to 0 in tight loops.
| Param | Type | Default | Description |
|---|---|---|---|
| x | int | required | Horizontal position (pixels from left) |
| y | int | required | Vertical position (pixels from top) |
| delay_ms | int | 1000 | Delay after click in milliseconds |
mas.click(540, 1200, delay_ms=0) # no pause
mas.swipe(from_coords, to_coords, duration_ms=1000) None
Drag from one point to another. Both points are (x, y) tuples. duration_ms controls speed: short for a flick, long for a controlled drag.
| Param | Type | Default | Description |
|---|---|---|---|
| from_coords | tuple[int, int] | required | Starting (x, y) coordinates |
| to_coords | tuple[int, int] | required | Ending (x, y) coordinates |
| duration_ms | int | 1000 | Swipe duration in milliseconds |
mas.input_text(text, delay_ms=0) None
Type into the currently focused field. Tap the field first to focus it.
mas.input_text("user@example.com")
mas.key_press(key_code, modifiers=None, duration_ms=100) None
Press a hardware key. duration_ms is the hold time — raise it for a long-press.
| Param | Type | Default | Description |
|---|---|---|---|
| key_code | KeyCode | required | Key to press |
| modifiers | list[Modifier] | None | None | Optional modifier keys |
| duration_ms | int | 100 | Hold time |
mas.key_press(KeyCode.BACK)
mas.key_press(KeyCode.POWER, duration_ms=3000) # long-press
Computer Vision
Macros decide what to do by looking at the screen. You give the SDK a template image (registered in the Image Library) and it finds where that image appears on the device.
mas.take_screenshot() Screenshot
Capture the screen as an in-memory base64 PNG. The main reason to call this explicitly is to reuse one screenshot across several searches and avoid re-capturing for each.
play = mas.find_object(images.play, screenshot=shot)
coins = mas.find_object(images.coins, screenshot=shot)
mas.find_object(image_id, *, threshold=0.8, ...) ObjectMatch | None
Find one template on screen. Returns an ObjectMatch or None if nothing matched at or above threshold.
| Param | Type | Default | Description |
|---|---|---|---|
| image_id | int | ImageRef | required | Image Library ID or ImageRef |
| threshold | float | 0.8 | Match confidence 0.0–1.0 |
| screenshot | Screenshot | None | None | Reuse a captured frame |
| continuous_mode | bool | False | Retry until found or timeout_ms |
| timeout_ms | int | 1000 | Budget for continuous_mode |
| capture_interval_ms | int | 500 | Gap between retries |
| max_matches | int | 1 | Max matches to return |
| search_region | Region | None | None | Restrict the search; faster and fewer false hits |
if match: mas.click(*match.center)
# only search the top-left quadrant
mas.find_object(images.badge, search_region=Region(0, 0, 500, 400))
mas.find_objects(image_id, *, max_matches=10, ...) list[ObjectMatch]
Same parameters as find_object but returns every match, sorted best-confidence first. Default max_matches is 10.
mas.click(*star.center)
mas.find_any_object(image_ids, *, search_strategy="first_match", ...) ObjectMatch | None
Search for several templates at once — handy when a UI element has variants (themes, languages, states). Takes a list. The returned match's .matched_template_id tells you which one hit.
| Strategy | Behavior |
|---|---|
| "first_match" | Return the first template that matches (fastest) |
| "best_match" | Try all, return the highest-confidence hit |
| "priority_order" | Prefer earlier templates in the list |
[images.claim_en, images.claim_fr, images.claim_es],
search_strategy="best_match",
)
mas.wait_for_object(image_id, timeout_ms=10000, threshold=0.8) ObjectMatch | None
Block until a template appears, or timeout_ms elapses. The wait happens server-side, so it is more efficient than polling find_object yourself. The right call for "do something, then wait for the next screen".
home = mas.wait_for_object(images.home_screen, timeout_ms=30000)
if home is None: raise RuntimeError("home never loaded")
mas.read_text(region=None, screenshot=None, model="eng_best", psm=7, color_conversion=ColorConversion.NONE, timeout_ms=30000) TextRecognitionResult
OCR — read text off the screen. Always pass a region; OCR on a tight box is faster and far more accurate than reading the whole screen.
| Param | Type | Default | Description |
|---|---|---|---|
| region | Region | None | None | Area to read — omit to read the whole screen (slow) |
| screenshot | Screenshot | None | None | Reuse a frame instead of capturing |
| model | str | "eng_best" | "eng_best" (accurate), "eng_fast", "eng" |
| psm | int | 7 | Page-segmentation mode (see below) |
| color_conversion | ColorConversion | NONE | Preprocessing step (try BGR_TO_GRAY on busy backgrounds) |
| timeout_ms | int | 30000 | OCR time budget |
psm — tells the engine the shape of the text: 7 single line (default), 8 single word, 6 uniform block, 11 scattered text.
result = mas.read_text(region=Region(420, 90, 660, 140))
score = int(result.text) if result.text.strip().isdigit() else 0
# single word on a noisy background
word = mas.read_text(region=Region(100, 200, 400, 260),
psm=8, color_conversion=ColorConversion.BGR_TO_GRAY)
Image Library
Template images do not live in your macro folder. They live in the Image Library inside Macro Automation Studio, where each image has a numeric ID. Vision calls take that ID.
You could pass raw integers, but that scatters meaningless numbers through your code. Declare them once with mas.images() and refer to them by name.
mas.images(mapping) ImageMap
Declare your images by name. Returns an ImageMap — a read-only object with one ImageRef per entry. Names must be valid Python identifiers.
images = mas.images({
"login_btn": 123,
"home_screen": 456,
"daily_reward": 789,
})
match = mas.find_object(images.login_btn)
Put all your macro's images in one mas.images({...}) call near the top of your script. Studio uses that call to know which images to bundle when the macro is published.
App Management
mas.open_app(package_name, timeout_ms=2000) None
Launch an Android app by package name.
mas.open_app("com.android.chrome", timeout_ms=5000)
mas.close_app(package_name) None
Force-stop an app.
mas.get_current_app() str
Package name of whatever is in the foreground.
mas.get_app_state(package_name) int
How the app is currently running. Compare against the module constants:
| Constant | Value | Meaning |
|---|---|---|
| mas.RUNNING_IN_FOREGROUND | 4 | Visible and focused |
| mas.RUNNING_IN_BACKGROUND | 3 | Running, not visible |
| mas.RUNNING_IN_BACKGROUND_SUSPENDED | 2 | Backgrounded and frozen |
| mas.NOT_RUNNING | 1 | Installed, not running |
| mas.NOT_INSTALLED | 0 | Not on the device |
mas.open_app("com.example.game")
mas.is_app_focused(package_name) bool
True if that app currently has input focus. Good for guarding a tap or typing.
while not mas.is_app_focused("com.example.game"):
time.sleep(0.5)
Persistent Storage
mas.save / mas.retrieve give a macro memory across runs — a streak counter, a checkpoint, a "last processed" marker. Data is any JSON-serializable dict.
Every entry is keyed by the triple (machine_id, port, task_name). Leave machine_id and port at their defaults — they handle the common case (per-PC, per-device-port state) automatically.
mas.save(task_name, data, machine_id=None, port=0) bool
Persist a dict. Re-saving the same key updates the entry. Raises ValueError if data is not JSON-serializable.
mas.retrieve(task_name, machine_id=None, port=0) dict
Read an entry back, or an empty dict if nothing was stored. The load-or-default pattern is one line.
streak = state.get("streak", 0) + 1
mas.save("daily_login", {"streak": streak})
mas.retrieve_all(task_name) list[dict]
Every entry for a task name, across all machines and ports. Each item is { "machine_id", "port", "data", "updated_at" }. Use it to roll up results from many emulators running the same macro.
mas.clear(task_name, ...)
Delete a stored entry.
mas.get_current_device_port()
The current device's port (e.g. 5556). Useful for labelling per-device output.
Utilities
mas.get_clipboard() str
Read the device clipboard as text.
Reading Argument-Form Values
If your macro has a .uibproj argument form, the UI Builder generated src/script_args.py. Import it to read the user's inputs, organized by the form's tabs.
target_url = args.general.target_url # tab "General", key "target_url"
max_items = args.general.max_items # Number Input → int
headless = args.advanced.headless # Checkbox → bool
script_args.py parses the command line at import time, so args is ready to use immediately. It is generated — regenerate it from the UI Builder whenever the form changes; do not hand-edit it.
Runtime UI — mas.ui.*
The mas.ui.* namespace drives a runtime dashboard — the live panel shown next to the device while your macro runs. You design the dashboard in the UI Builder (a .uibrt file), give every widget a name, then drive each one by that name from your script.
A name typo between the .uibrt and the call is the most common dashboard bug — the call simply updates nothing.
Widget → Method Map
| To update… | Call |
|---|---|
| a Runtime Label | mas.ui.set_text(name, text) |
| a Progress Bar | mas.ui.set_progress(name, value) |
| a Chart (stream) | mas.ui.add_data_point(name, value, label, series=None) |
| a Chart (replace) | mas.ui.set_chart_data(name, data) · clear_chart(name) |
| a Text Area (append) | mas.ui.append_text(name, line) |
| a Text Area (replace) | mas.ui.set_textarea(name, text) · clear_text(name) |
| a Table | mas.ui.set_table_data · append_table_row · clear_table |
| read a Button | mas.ui.wait_for_event() · on_click(name, handler) |
| read a Text Input | mas.ui.get_input_value(name) · on_change(name, handler) |
Display widgets — the script writes, the user reads
mas.ui.set_text(name, text)
Update a Runtime Label, the caption of a progress bar, a text area, or a button label.
mas.ui.set_progress(name, value)
Update a Progress Bar. Studio clamps value to the widget's [min, max].
mas.ui.add_data_point · set_chart_data · clear_chart
A chart holds a series of { label, value } points. Stream them as they arrive or replace the dataset at once. series groups points into named lines/bars.
mas.ui.add_data_point("cpu_chart", value=cpu, label=f"t={t}", series="CPU")
# replace
mas.ui.set_chart_data("totals", [{"label": "Gold", "value": 1250}])
mas.ui.append_text · set_textarea · clear_text
A text area is the natural place for a running log. append_text adds a line and auto-scrolls; lines past maxLines drop off the top.
mas.ui.set_textarea("log", "fresh contents")
mas.ui.set_table_data · append_table_row · clear_table
A table has fixed columns (set in the UI Builder). Rows are dicts whose keys match those column names. Use mas.ui.cell_button(...) inside a cell to get a per-row button.
{"Item": "Gold", "Count": "1,250", "Status": "OK"},
])
mas.ui.append_table_row("results", {"Item": "Stone", "Count": "12", "Status": "low"})
Interactive widgets — the user acts, the script reads
Two models: blocking (the script waits for the user) and callback (handlers fire on a background thread while the script keeps working). Pick one per macro and stay with it.
mas.ui.wait_for_event(timeout=None) UIEvent | None
Model A — blocking. Your script stops and waits for the user. Returns a UIEvent or None on timeout.
if event and event.widget_id == "start_btn":
begin_run()
mas.ui.on_click · on_change · start_listener
Model B — callback. Register handlers, start a background listener, and keep working. The handlers fire on a daemon thread. This is the right model for a Stop or Pause button that must be reactive during a long task.
def on_stop(): global stop; stop = True
mas.ui.on_click("stop_btn", on_stop)
mas.ui.start_listener() # register handlers BEFORE this
for i in range(1000):
if stop: break
do_work(i)
start_listener() is idempotent and returns immediately. The listener runs on its own RPC connection on a daemon thread. A handler that raises is caught and logged.
mas.ui.get_input_value(name) str | None
Read the current value of a Text Input on demand — no listener required.
with mas.ui.batch(): ... context manager
Each mas.ui.* call is one round-trip to Studio. Wrap multiple widget updates in batch() to send them as one call — the difference between a smooth dashboard and a flickering one.
mas.ui.set_text("status", f"Step {i}/{n}")
mas.ui.set_progress("main_bar", i * 100 // n)
mas.ui.add_data_point("rate", value=rate, label=str(i))
batch() blocks cannot be nested. The buffer is process-global — finish the with before letting listener callbacks fire their own updates.
Exceptions
None means "not found, that's fine" — handle it with if. An exception means "something is wrong" — let it stop the macro unless you have a real recovery.
Every SDK error descends from MASError. RPC-level failures are RPCError (with .code, .message, .data).
mas.open_app("com.example.game")
home = mas.wait_for_object(images.home_screen, timeout_ms=30000)
if home is None: raise RuntimeError("home never loaded")
except mas.ImageNotFoundError as e:
print(f"image missing: {e}"); raise
except mas.DeviceNotConnectedError:
print("lost the device"); raise