Skip to main content

python-mobius

A reverse-engineered Python client for the BLE protocol used by "Mobius Ready" aquarium equipment — EcoTech Marine (VorTech pumps, Radion lights), AquaIllumination (Prime, Hydra), Neptune Systems, and NYOS.

Built on bleak for cross-platform BLE.

Not affiliated with or endorsed by any of these companies. This is an independent reimplementation of the wire protocol for interoperability with hardware you own, derived from public community reverse-engineering work and analysis of the publicly-distributed Mobius Android app. See documentation/ for the full protocol writeup, with every field marked as either directly confirmed or explicitly flagged as inferred/experimental.

Status

Alpha. Core protocol (framing, CRC, attribute get/set, scenes), pump telemetry, pump schedules, light schedules, device discovery/grouping (both EcoTech Marine's and AquaIllumination's own BLE company IDs), and Thread/CoAP relay (reading a non-gateway tank member through the gateway's own connection) are implemented and verified against real hardware (two VorTech MP40QD pumps, two Radion XR15 G6 Pro lights, one AquaIllumination Axis 20 pump). Two write operations are also confirmed against real hardware: rebooting a device, and syncing a device's own clock to the current time. See documentation/10-known-gaps-and-open-questions.md for what isn't covered yet (dosers, environmental sensors) or is implemented but not yet verified against real hardware (Vectra/NYOS Quantum-specific settings).

Install

pip install python-mobius
# or, for more robust BLE connection retries (recommended):
pip install python-mobius[retry]

Quick start

import asyncio
from mobius import scan_for_mobius_devices_with_info, group_by_pan_id, MobiusDevice

async def main():
    found = await scan_for_mobius_devices_with_info()
    for pan_id, members in group_by_pan_id(found).items():
        print(f"tank {pan_id:#06x}:")
        for device, info in members:
            print(f"  {device.address}  {info.model.name}  {info.serial}")

    device, _info = found[0]
    async with MobiusDevice(device) as d:
        summary = await d.get_device_summary()
        print(summary)

asyncio.run(main())

Or from the command line:

mobius-scan --adapter hci0

What you can do

  • Discover devices and group them by tank/mesh (pan_id), reading model/serial straight from BLE advertisements — no connection required.
  • Read pump telemetry: current speed, estimated flow (GPH), operation state, error state, and (Vectra/NYOS Quantum pumps) motor power in watts.
  • Read pump schedules: which mode (constant speed, tidal swell, pulse, etc.) is active at any given time, exactly as programmed.
  • Read light schedules: per-channel intensity at any given time, replicating the app's own client-side interpolation (there's no "current intensity" attribute — lights only expose the programmed curve).
  • Read every configured scene (get_configured_scenes()) — name, timeout, and its own light/pump payload, at whatever slot it lives in, plus the currently active one (get_current_scene()).
  • Control scenes: start feed mode, resume the normal schedule, or any other configured scene — one write, broadcast to the whole mesh by default (broadcast=True). Uses the same mesh-propagation mechanism confirmed against real hardware for the clock-sync write below; applying it to scene activation specifically hasn't been independently verified the same way yet.
  • Fetch everything one device poll needs in a single round-trip (get_full_poll_batch()) — identity, metadata, light/pump state, and scene data together, confirmed 2-2.6x faster on real hardware than reading each piece separately.
  • Read device-specific settings: VorTech's own "Local Control"/"Led Auto Dim" and Radion's own "Max Fan Speed"/"Fan Shutdown" (get_advanced_features()), plus Vectra and NYOS Quantum settings (get_vectra_info()/get_coffee_info() — no real hardware to verify either against, see known gaps).
  • Fix a desynced device clock (set_time_to_now()) — a WRITE. Writing to one device appears to propagate to the rest of its Thread mesh too, confirmed against real hardware — see 09-thread-coap-relay.md for what's confirmed and what isn't yet.
  • Reboot a device (reboot()) — a WRITE, matching the app's own "Restart" button exactly. Confirmed against real hardware directly connected; not yet confirmed via relay.
  • Dump every attribute a device supports (dump_attributes()), matching the app's own Settings → Troubleshoot diagnostic feature's data collection — confirmed against real hardware. Output is enriched JSON (resolved names, decoded values where known), not the app's own plain-text format. See 13-attribute-dump.md.
  • Low-level protocol access (build_frame, get_attribute, set_attribute, ...) if you want to go beyond what's wrapped in MobiusDevice.

Supported device types

PrimitiveType Support Notes
VisualV1 (Radion, Prime, Hydra, etc.) ✅ Verified Lights
VorTechV1, TurtleV1 (AquaIllumination Axis) ✅ Verified Pumps -- confirmed against real hardware directly (VorTech MP40QD, AquaIllumination Axis 20)
PumpV1, VectraV1, AlpacaV1 (AquaIllumination Orbit) ✅ Verified Pumps -- same wire format as the primitives above, not independently confirmed against their own real hardware
CoffeeV1 (NYOS Quantum) ⚠️ Experimental Same wire structure as pumps per the protocol, untested against real hardware
DoseV1, HotSauceV1 ❌ Unsupported Different primitive format; identity info only

MobiusDevice.get_device_summary() always tells you which tier applies via its "support" field — see documentation/04-device-identity.md.

"Verified" above is about core telemetry/schedule parsing. The Vectra/NYOS Quantum-specific settings methods (get_vectra_info()/get_coffee_info()) and motor_power_watts are newer, implemented from the decompiled source alone, and not verified against real Vectra or NYOS Quantum hardware regardless of the table above — see known gaps.

Development

git clone https://code.r3pek.org/r3pek/python-mobius
cd python-mobius
pip install -e ".[dev]"
pytest

Tests are validated against real captured packets and real device manufacturer-data/serials where possible — see tests/.

License

GPLv2 — see LICENSE.

Acknowledgments

The protocol reverse-engineering and implementation in this library were carried out with substantial assistance from Claude (Anthropic), used to analyze a decompiled copy of the official Mobius Android app and cross-reference it against prior public community research (notably the Reef2Reef "Controlling Mobius enabled VorTech pump using 0-10V and BLE" thread and the danmrossi/MobiusControl project), then to design, write, and test the Python implementation itself. See documentation/00-overview.md for the full methodology and confirmation-strength notes on every protocol detail.

Metadata

Release files for python-mobius 0.7.2

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for python-mobius 0.7.2
File Size Uploaded
python_mobius-0.7.2.tar.gz 263.2 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for python-mobius 0.7.2
File Interpreter ABI Platform
python_mobius-0.7.2-py3-none-any.whl Python 3 none any Details

Total release size: 404.9 kB

Release files / python_mobius-0.7.2.tar.gz

Download URL python_mobius-0.7.2.tar.gz
Size 263.2 kB
Tags Source
SHA-256 checksum
How to use checksums
01aeb5440ef5d76b34df4c247e0e3a397c75c936306dac4c5607a6ef4206f183
BLAKE2b-256 checksum
How to use checksums
b6484513c5c50cf162bd4584352e5649729b618cefe69c77175394a632320ac6
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.12.14

Release files / python_mobius-0.7.2-py3-none-any.whl

Download URL python_mobius-0.7.2-py3-none-any.whl
Size 141.7 kB
Tags Python 3
SHA-256 checksum
How to use checksums
3ba7c975f8a08143a86c8f8075f5575d9f3e9e04a7986cc294ff860b0003c679
BLAKE2b-256 checksum
How to use checksums
dcb23af9594cfdaf9a831cdc5a1e15b1a511a1da623903100521c46eb5b08851
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.12.14

Release history Release notifications | RSS feed

0.10.1

2 release files

0.10.0

2 release files

0.9.0

2 release files

0.8.4

2 release files

0.8.3

2 release files

0.8.2

2 release files

0.8.1

2 release files

0.8.0

2 release files

This release

0.7.2 This release

2 release files

0.7.1

2 release files

0.7.0

2 release files

0.6.0

2 release files

0.5.0

2 release files

0.4.4

2 release files

0.4.3

2 release files

0.4.2

2 release files

0.4.1

2 release files

0.4.0

2 release files

0.3.1

2 release files

0.3.0

2 release files

0.2.1

2 release files

0.2.0

2 release files

0.1.4

2 release files

0.1.3

2 release files

0.1.2

2 release files

0.1.1

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page