Skip to content

Repository files navigation

libasi

A Python client library for controlling ZWO ASIAIR telescope controllers. Reverse-engineered from the ASIAIR Android app and validated against live hardware.

289 commands covering every ASIAIR function: mount control, camera operation, autoguiding, plate solving, planning, power management, focuser, filter wheel, rotator, polar alignment, and more.

Zero dependencies beyond Python 3.10+ standard library.

Quick Start

from asiair import ASIAIRClient

with ASIAIRClient("192.168.251.186") as air:
    # System info
    info = air.system.info()
    print(f"ASIAIR: {info.model}  CPU: {info.cpu_temp}C")

    # Mount position
    mi = air.mount.info()
    print(f"RA={mi.ra:.4f}h  Dec={mi.dec:.4f}  Tracking: {mi.is_tracking}")

    # Focuser temperature
    print(f"Focuser temp: {air.focuser.temperature:.1f}C")

    # Toggle dew heater
    air.power.set_port(3, on=True, value=100)

Installation

# Clone the repo
git clone https://github.com/jewzaam/libasi.git
cd libasi

# Install (no dependencies needed)
pip install -e .

Or just copy the asiair/ directory into your project.

Finding Your ASIAIR

The ASIAIR creates a WiFi network (default SSID starts with "ASIAIR-"). Connect to it and the ASIAIR is at 10.0.0.1.

If using station mode (ASIAIR connected to your home WiFi), find its IP from your router's DHCP table or the ASIAIR app's connection screen.

Architecture

ASIAIRClient
  |-- main    (port 4700) -- camera, power, system, planning, file listing
  |-- guider  (port 4400) -- guiding, mount control, calibration
  |-- images  (port 4801) -- FITS/JPG download (binary protocol)
  |
  |-- .mount         Mount control (slew, goto, park, track)
  |-- .camera        Main imaging camera
  |-- .guide         Autoguider (PHD2-based)
  |-- .focuser       Electronic Auto Focuser (EAF)
  |-- .power         DC power outputs (dew heaters!)
  |-- .planner       Imaging plans and sequences
  |-- .solver        Plate solving and annotation
  |-- .stacker       Live and batch stacking
  |-- .caa           Camera Angle Adjuster (rotator)
  |-- .wheel         Electronic Filter Wheel (EFW)
  |-- .polar_align   Polar alignment routines
  |-- .images        Image file management
  |-- .system        System info, WiFi, time, storage

Protocol

Communication is JSON-RPC 2.0 over TCP, newline-delimited. The ASIAIR runs two command servers:

Port Purpose Protocol
4700 Main camera, power, system, planning, file listing JSON-RPC
4400 Guider, mount control, calibration JSON-RPC
4801 Image file download (FITS, JPG, thumbnail) Binary framed

Additional ports exist for image streaming (4800/4500), reporting (4900), and firmware updates (4350/4360).

No authentication required by default. If you've set a device lock password in the ASIAIR app, you'll need to handle that separately.

See ASIAIR_PROTOCOL.md for the full protocol reference and CONFIRMED_FORMATS.md for wire-captured request/response formats.

Module Reference

Mount

# Full state (RA, Dec, Az, Alt, tracking, park, caps, voltage, etc.)
mi = air.mount.info()
print(f"RA={mi.ra}h Dec={mi.dec} Alt={mi.alt} Az={mi.az}")
print(f"Model: {mi.model}  Firmware: {mi.firmware}")
print(f"Tracking: {mi.is_tracking} ({mi.track_mode})")
print(f"Park: {mi.park_status}  Voltage: {mi.input_voltage_mv/1000:.1f}V")

# Quick position reads
ra, dec = air.mount.position()
ra, dec = air.mount.get_ra_dec()
alt, az = air.mount.altaz()

# Slewing
air.mount.move("north", 3)          # slew north for 3 seconds
air.mount.move("west", 5)           # slew west for 5 seconds
air.mount.stop()                     # emergency stop
air.mount.set_slew_rate(7)          # 0=1x, 7=MAX

# GoTo (plate-solve and center)
air.mount.goto(5.9, 32.55)          # RA hours, Dec degrees
air.mount.stop_goto()                # cancel

# Direct GoTo (blind slew, no plate solve)
air.mount.scope_goto(5.9, 32.55)

# Tracking
air.mount.set_tracking(True)
air.mount.set_track_mode(0)         # 0=Sidereal, 1=Solar, 2=Lunar
air.mount.set_guide_rate(0.5)       # fraction of sidereal

# Parking
air.mount.park()
air.mount.unpark()
air.mount.set_park_position()        # save current position as park
air.mount.can_park()                 # True/False

# Sync
air.mount.sync(5.9, 32.55)

# Connection
air.mount.get_connection_mode()      # Serial/Ethernet/BLE
air.mount.scan_ports()               # find serial ports
air.mount.get_mount_list()           # 72 supported mount drivers

Camera

# List and connect
cameras = air.camera.list_cameras()
air.camera.open(2)                   # open by camera ID
air.camera.close()

# Info
info = air.camera.info()             # chip size, bins, pixel size, cooler, color
controls = air.camera.controls()     # all adjustable parameters with min/max

# Read/write controls
air.camera.gain = 100
air.camera.exposure_us = 5000000     # 5 seconds (microseconds)
air.camera.bin = 2
temp = air.camera.temperature        # sensor temp in Celsius
power = air.camera.cooler_power      # cooler power %

# Cooler
air.camera.set_cooler(True, target_temp=-10)
air.camera.set_anti_dew(True)        # built-in anti-dew heater

# Exposure
air.camera.start_exposure("light", save=True)
air.camera.start_exposure("dark", save=True)
air.camera.stop_exposure(keep_frame=True)

# Page mode
air.camera.set_page("preview")       # preview, focus, plan, live, video

# Streaming
air.camera.begin_streaming()
air.camera.stop_streaming()

Guider

# Connection
conn = air.guide.get_connected()
print(f"Camera: {conn.camera_name}  Mount: {conn.mount_model}")
state = air.guide.app_state()        # Stopped, Looping, Calibrating, Guiding

# Start/stop
air.guide.loop()                     # start looping (preview)
air.guide.find_star()                # auto-select guide star
air.guide.guide()                    # start guiding
air.guide.stop()                     # stop

# Calibration
air.guide.clear_calibration()
air.guide.flip_calibration()         # for pier side change
calibrated = air.guide.get_calibrated()

# Settings
setting = air.guide.get_setting()    # focal length, cal params, multistar
air.guide.set_algo_param("x", "minMove", 0.15)

# Camera controls
gain = air.guide.get_gain()          # {min, max, val}
air.guide.set_gain(100)
air.guide.set_binning(2)

Power (Dew Heaters)

# Read all 4 DC output ports
ports = air.power.get_ports()
for p in ports:
    print(f"Port {p.index}: {p.port_type} {'ON' if p.on else 'OFF'} {p.value}%")

# Set a single port (reads current type to avoid disconnection bug)
air.power.set_port(3, on=True, value=100)    # dew heater full power
air.power.set_port(3, on=True, value=50)     # 50% PWM
air.power.set_port(3, on=False)              # off

# Set multiple ports at once
air.power.set_multiple({
    0: (True, 100),     # port 0: on at 100%
    3: (True, 50),      # port 3: on at 50%
})

# Quick controls
air.power.all_on()
air.power.all_off()

Warning: Changing a port's type label (e.g., from "other" to "camera") disconnects all equipment. The library preserves existing types by default.

Focuser

# Info
fi = air.focuser.info()
print(f"Position: {fi.position}  Temp: {fi.temperature}C  Model: {fi.model}")

# Move
air.focuser.move_to(18500)           # absolute position
air.focuser.move_relative(100)       # relative steps
air.focuser.stop()

# Auto focus
air.focuser.auto_focus()             # uses current AF settings
air.focuser.stop_auto_focus()

# Settings
setting = air.focuser.get_setting()  # AF exposure, step size, triggers

Planner

The ASIAIR has two built-in automation levels:

  • Autorun — captures a sequence of frames on the current target (exposure loop with dithering, frame counting, save-to-disk)
  • Plan mode — orchestrates a series of autoruns across multiple targets, handling GoTo transitions, meridian flips, and end-of-session actions (park, cool down)

The planner module exposes plan CRUD and sequence configuration. This is useful for remote observatories where the ASIAIR must execute autonomously even if the controlling process goes offline. For most use cases, the library's primitives (GoTo, guide, capture, download) give you more flexibility than plan mode — see examples/imaging_session.py.

# List plans
plans = air.planner.list_plans()
for p in plans:
    print(f"[{p.plan_id}] {p.name}  enabled={p.enabled}")

# Create a simple plan
air.planner.create_simple_plan(
    plan_id=10, name="M31",
    ra_hours=0.7122, dec_degrees=41.269
)

# Sequences and settings
air.planner.set_sequence_setting(group_name="M31")
air.planner.set_dither(enable=True, amount=5, interval=1)

# Meridian flip
merid = air.planner.get_merid_delta()
print(f"Time to flip: {merid['target_merid_delta_min']:.0f} min")

Plate Solver

air.solver.solve()
result = air.solver.get_result()
print(f"Solved: RA={result['ra_dec'][0]}h Dec={result['ra_dec'][1]}")

air.solver.annotate()
annotations = air.solver.get_annotate_result()

System

# Info
info = air.system.info()
print(f"{info.model}  CPU: {info.cpu_temp}C")

# Dawn/dusk
dawn, dusk = air.system.dawn_dusk()

# WiFi
ap = air.system.wifi_ap()           # AP SSID and password
station = air.system.wifi_station_state()  # station mode IP, signal
networks = air.system.wifi_scan()    # available networks

# Storage
storage = air.system.image_save_path()
disk = air.system.disk_volume()

# Time
air.system.set_time(2026, 3, 25, 20, 0, 0, "America/Los_Angeles")

# System control
air.system.reboot()
air.system.shutdown()

CAA (Camera Angle Adjuster)

ci = air.caa.info()
print(f"Angle: {ci.current_angle}  Model: {ci.model}")
air.caa.move_to(180.0)              # rotate to angle
air.caa.stop()

Filter Wheel

wheels = air.wheel.list_connected()
air.wheel.open(0)
air.wheel.position = 3              # move to slot 3
air.wheel.calibrate()

Events

The ASIAIR pushes real-time notifications for exposure progress, mount state changes, guide corrections, etc.

def on_event(port_name, event):
    method = event.get("method", "")
    print(f"[{port_name}] {method}: {event}")

air.on_event(on_event)
# Events will now fire in the background as things happen

DewGuard — Automatic Dew Heater Controller

Included is a ready-to-use dew heater automation tool that:

  • Monitors the focuser temperature sensor
  • Fetches dew point from weather data (Open-Meteo API, free, no key needed)
  • Automatically ramps heater power as temperature approaches dew point
  • Supports PWM for smooth proportional control
# Auto mode — fetches weather, monitors temp, controls heater
python examples/dew_guard.py

# Manual humidity (no internet needed)
python examples/dew_guard.py --humidity 80

# Fixed dew point
python examples/dew_guard.py --dew-point 8

# Dry run — show what would happen without controlling anything
python examples/dew_guard.py --dry-run

# Custom settings
python examples/dew_guard.py --port 3 --margin 5 --poll 60

Programmatic usage

from asiair import ASIAIRClient, DewController, DewControllerConfig

air = ASIAIRClient("192.168.251.186")
air.connect()

config = DewControllerConfig(
    heater_port=3,
    margin_c=3.0,          # start ramping 3C above dew point
    max_power=100.0,
    poll_interval_sec=30,
    camera_anti_dew=True,   # also enable camera's built-in anti-dew
)

ctrl = DewController(air, config)
ctrl.start()

# Check status anytime
print(ctrl.status)
# {'ambient_c': 12.5, 'dew_point_c': 8.2, 'delta_c': 4.3,
#  'heater_on': False, 'heater_power': 0, ...}

ctrl.stop()

Example Scripts

Script Description
examples/imaging_session.py Full automated session: GoTo, guide, capture, and download
examples/capture_and_download.py Watch for new captures and download FITS files in real-time
examples/flat_panel.py DeepSkyDad FP2 flat panel control and flat frame automation
examples/dew_guard.py Automatic dew heater controller
examples/status_monitor.py Live dashboard showing mount, camera, focuser, power
examples/session_startup.py Automated session initialization (unpark, cool, connect, focus)
examples/sync_time.py Sync time, timezone, and location to mount (DST workaround)
examples/quick_goto.py Command-line plate-solve GoTo
examples/power_control.py Command-line power port management

RE Tools

Reverse-engineering tools for analyzing new ASIAIR firmware versions or new hardware:

Tool Description
tools/extract_commands.py Extract RPC methods, constants, events from APK/DEX files
tools/parse_traffic.py Parse pcap traffic captures into protocol documentation

How It Was Built

This library was reverse-engineered from the ASIAIR Android app (v2.5.2):

  1. APK decompilation — Extracted 298 JSON-RPC command names, event types, and data structures using androguard
  2. Traffic capture — Captured 5,330 live messages via PCAPdroid to confirm exact parameter formats
  3. Live testing — Every method validated against a real ASIAIR Plus (RK3568)

The protocol documentation is included:

Tested Equipment

  • ASIAIR Plus (RK3568), firmware 1341
  • ZWO ASI2600MC Duo (main camera)
  • ZWO ASI220MM Mini x2 (guide cameras)
  • ZWO AM5N (mount), firmware 1.8.8
  • ZWO EAF (focuser), firmware 3.3.8
  • ZWO CAA-M54 (rotator), firmware 1.1.1

The protocol is the same across all ASIAIR models (Mini, Plus, SE). Mount commands work with any ASIAIR-supported mount.

Limitations

  • Live image streaming (ports 4800/4500) for real-time preview display is not implemented — use the ASIAIR app for live view
  • File downloads (port 4801) are implemented for FITS, JPG, and thumbnails
  • Some commands may not be available on older ASIAIR firmware versions
  • Event payloads vary by firmware version; the transport delivers raw dicts
  • Dangerous operations (slew, goto, exposure) check state first and raise BusyError if a conflicting operation is in progress — pass force=True to bypass

Contributing

Contributions welcome! If you have a different ASIAIR model or equipment, running the test suite and reporting results helps:

python test_asiair.py --host YOUR_ASIAIR_IP
python test_asiair.py --host YOUR_ASIAIR_IP --full  # includes write tests

License

MIT License. See LICENSE.

Disclaimer

This is an unofficial, community-built library. It is not affiliated with, endorsed by, or supported by ZWO. Use at your own risk. The authors are not responsible for any damage to your equipment.

About

python library for controlling ASI Plus devices without a phone app.

Resources

Contributing

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages